A custom structural directive is only as safe as its template context typing. `ngTemplateContextGuard` is a static type-guard method the Angular compiler calls at compile time (via the Ivy template type-checker) to verify that variables declared with `let-foo` inside the directive's template actually match the context shape emitted from `createEmbeddedView`. Without it, `<ng-template let-x>` compiles even if `x` doesn't exist on the context object, and the mismatch only surfaces at runtime. Pairing this with `ngTemplateGuard_ifRole: 'binding'` also tells the compiler to narrow the type of the bound expression itself, so the template body sees `role` as `string`, not `string | null`.
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';interface IfRoleContext {$implicit: boolean;ifRole: string;}@Directive({selector: '[ifRole]',standalone: true,})export class IfRoleDirective {private hasView = false;constructor(private templateRef: TemplateRef<IfRoleContext>,private viewContainer: ViewContainerRef,) {}@Input() set ifRole(role: string) {const allowed = role === 'admin';if (allowed && !this.hasView) {this.viewContainer.createEmbeddedView(this.templateRef, {$implicit: true,ifRole: role,});this.hasView = true;} else if (!allowed && this.hasView) {this.viewContainer.clear();this.hasView = false;}}static ngTemplateGuard_ifRole: 'binding';static ngTemplateContextGuard(dir: IfRoleDirective,ctx: unknown,): ctx is IfRoleContext {return true;}}