Published
- 4 min read
Strongly typed ng-template in Angular
![Strongly typed ng-template in Angular](/i/l/7/post_detail/angular_wordmark_gradient.png)
If you're like me and feel uneasy whenever something is typed as 'any' in TypeScript, this article is for you! I'll show you a neat trick to help both the compiler and your IDE understand the exact type of data being passed to your ng-template.
The problem
When you have a <ng-template>
that accepts parameters via context, you usually lose TypeScript's type safety, reverting to the prehistoric age of JavaScript with no type enforcement:
<ng-template #someTemplate let-someVariable="someVariable">
{{Math.abs(someVariable)}} <!-- compiler and IDE have no idea that the variable is a string -->
</ng-template>
With this approach, you can perform any operation on someVariable
, and the compiler won't warn you—even if it results in runtime errors.
The solution
To ensure type safety, we can create a type assertion guard directive:
@Directive({
selector: 'ng-template[some-template]',
standalone: true,
})
export class SomeTemplateNgTemplate {
static ngTemplateContextGuard(
directive: SomeTemplateNgTemplate,
context: unknown
): context is {someVariable: string} {
return true;
}
}
Explanation
- Directive setup
- This directive applies to
<ng-template>
elements that include thesome-template
attribute (ng-template[some-template]
in the selector). - It's marked as
standalone
, which is the recommended approach in modern Angular.
- This directive applies to
- Type Context Guard
- The class name is not important and can be anything.
- The
static ngTemplateContextGuard
function is where the magic happens. - It must accept two parameters:
- An instance of itself (
directive: SomeTemplateNgTemplate
). - The
context
(which is typed asunknown
which is a more type-safeany
).
- An instance of itself (
- The return type uses a TypeScript type predicate, which tells the compiler: If this function returns
true
, then thecontext
must match the given type{ someVariable: string }
.
Since this function always returns true
, TypeScript will assume that every template using this directive has the expected type.
Important note: As with all TypeScript type assertions, this is a compile-time safety measure—it does not enforce types at runtime. You can still pass invalid values, but TypeScript will warn you beforehand.
Applying the Directive
Now, update your template to use the directive:
<ng-template some-template #someTemplate let-someVariable="someVariable">
{{Math.abs(someVariable)}}
</ng-template>
The result
With the some-template
directive in place, Angular now correctly infers the type of someVariable
. If you try to use Math.abs(someVariable)
, TypeScript will now show an error:
NG5: Argument of type 'string' is not assignable to parameter of type 'number'.
Conclusion
By leveraging ngTemplateContextGuard
, you can enforce strong typing within ng-template
contexts, making your Angular code safer and more maintainable. This simple trick helps catch potential errors at compile time rather than at runtime—ensuring better developer experience and fewer unexpected bugs.