'Is there a way to typehint IDE for "let variable" type in Typescript and Angular?
Having markup like
<mat-cell *matCellDef="let request">
<a [href]="request.url" target="_blank">{{request.requestId}}</a>
</mat-cell>
Can I typehint IDE somehow that request is of type Request? I am using IntelliJ here.
Please be noted, that I am using Angular Material table here, so declaring request in component is not an option here as it is purely template variable. It contains row data provided internally by component itself on every row iteration.
Note that this is perfectly valid markup used in MatDataTable component.
Solution 1:[1]
as the
tableDataSource: MatTableDataSource<ToDoInterface>;
does not type the model,
this:
<ng-container matColumnDef="toDo">
<th mat-header-cell *matHeaderCellDef mat-sort-header>ToDo</th>
<td mat-cell *matCellDef="let model">
<ng-container *ngIf="assertItemType(model) as toDoModel">
{{toDoModel.toDo}}
</ng-container>
</td>
</ng-container>
where:
assertItemType(item: ToDoInterface): ToDoInterface {
return item;
}
works.
but not sure if it the best way to do it
Solution 2:[2]
This can be solved by wrapping your variable inside another ng-template
The type assertion is noticed by the IDE when *ngFor or *ngIf is in use. This is somehow another workaround, but I liked a lot more than other solutions because it just adds 2 more lines of code in the HTML, of course if you're using your variable only 1 or 2 times this other solution is better. My answer:
Instead of this:
<ng-template *ngTemplateOutlet="foo; context: {$implicit: {fooProp: 'Hello!'}}"></p>
<ng-template #foo let-args>
This is untyped: {{ args.fooProp }}<br>
</ng-template>
Do this:
<ng-template *ngTemplateOutlet="foo; context: {$implicit: {fooProp: 'Hello!'}}"></p>
<ng-template #foo let-untypedArgs>
<ng-template [ngIf]="identity(untypedArgs)" let-args="ngIf">
This is typed: {{ args.fooProp }}<br>
</ng-template>
</ng-template>
identity(foo: Foo): Foo {
return foo;
}
With this, now if you add an invalid property to your context, you'll get the following compilation error which is great, here's a stackblitz demo, The downside with this solution is that the inner <ng-template> is rendered later because of the [ngIf].
Property 'newFooProp' does not exist on type 'Foo'.
This is the same answer I gave at this other question.
Solution 3:[3]
Although the question is bit older I like to add a method that I used to achieve IDE type-hints.
First we can just add an additional method like,
asRequest(request: any): Request{
return workflow as Workflow;
}
So, now you can wrap the request with that method.
<mat-cell *matCellDef="let request">
<a [href]="asRequest(request).url" target="_blank">{{asRequest(request).requestId}}</a>
</mat-cell>
So the just request is now asRequest(request). (You can think like its a function for casting. Actually it is!)
I know this way makes some extra function calls, but it will do what you needed.
Solution 4:[4]
Should be careful with functions in templates. Here's a custom pipe I use. It keeps refactoring sane in my IDE (PhpStorm):
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'asType',
pure: true,
})
export class AsTypePipe implements PipeTransform
{
transform<T>(value: any, clss: new (...args: any[]) => T): T
{
return value as T;
}
}
In your template you can use as:
<ng-template let-item="someAnyType">
<span>{{(item | asType :Customer).name}}</span>
</ng-template>
where you define:
import {Customer} from 'somewhere';
public Customer: typeof Customer = Customer; //Type variable to use in templates
public customer: Customer; //Case sensitivity allows creating similar variables if needed.
Solution 5:[5]
Now I finally understand the issue (after the OP editing).
You can't specify a type in the template when declaring *matCellDef="let request", but, that local variable should be already typed in your component.
When using the AM Table, we'll have something like this, right?
<mat-table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
<mat-cell *matCellDef="let user"> {{user.name}} </mat-cell>
</ng-container>
</mat-table>
We can't type the user local variable, but we can type dataSource.
So, Imagine that we have this interface:
export interface User {
name: string;
email: string;
}
And that our dataSource is a collection of users. Then we would type it like this:
dataSource: User[] = this.getUsers();
Now we're telling the compiler that each element of dataSource is of type User.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Lean Pilar |
| Solution 2 | |
| Solution 3 | Tharindu Sathischandra |
| Solution 4 | user8507746 |
| Solution 5 |
