Reputation: 7609
I have an app in angular where I set
"angularCompilerOptions": {
"strictInjectionParameters": true,
"fullTemplateTypeCheck": true,
"strictTemplates": true
}
So every input / output is not type checked.
It is good for most of the app, but I have some app input like (here I show select, but I also have a simple app-input
that works the same) :
.html
<app-select
[useSearch]="true"
[formControlName]="'country'"
(valueChange)="setSelectedCountry($event)" <=== $event is of type unknown
>
<app-option
*ngFor="let country of locations$ | async"
[name]="'COUNTRIES.' + country.code | translate"
[value]="country.code" <=== this is of type Country
></app-option>
</app-select>
.ts
setSelectedCountry(code: Country) {
this.store.dispatch(loadLocationRequest({ payload: { code } }));
this.selectedLocation$ = this.store.pipe(select(getLocationByCode(), { code }));
}
in the above, since I use my app-select
for multiple selector with various value, it is type :
@Input()
get value(): unknown | unknown[] {
return this.pValue;
}
set value(newValue: unknown | unknown[]) {
if (newValue !== this.pValue) {
this.pValue = newValue;
this.writeValue(newValue);
}
}
Now, There is 2 solutions I see,
[(value)]="country"
and I make a method that typecheck in all my components that use a select:But I would like to have something easier for those case only.
Is it possible to pass a Generic type to a component via input or something, so that it return a type of the Generic I passed ?
like (ex : <app-select<string>>
)
Is it possible to make a pipe that cast a to a generic value ? without having to make a pipe for each type ? string
number
etc... ?
Is it possible to ignore certain checks ?
Upvotes: 1
Views: 3760
Reputation: 7609
So, I tried multiple thing, and I came with a hacky way, I do not really recommend, unless in some special case (a bit like the $any()).
The correct way would be :
a Pipe for each unknown type, so that inside the type you can do some types check for ex
import { Pipe, PipeTransform } from '@angular/core'
import { Gender } from '@app/__generated__/globalTypes'
@Pipe({
name: 'toAnimal',
pure: true,
})
export class ToAnimalPipe implements PipeTransform {
transform(value: unknown): Animal {
return typeof value === "string" && Object.keys(Animal).includes(value) ?
value as Animal :
Animal.DOG // Default value just use anything you want.
}
}
The hacky way :
I made the following simple pipe :
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'toType',
pure: true,
})
export class ToTypePipe implements PipeTransform {
transform<T>(value: unknown, _: T): T {
return value as T;
}
}
You can call it like
value | toType: Animal.DOG
value can be anything of Animal
but we just cast is as one of the value, so the compiler consider it ok to use it and do not complain.
IT IS NOT SAFE AND KIND OF REMOVE THE PURPOSE OF STRICT MODE.
But, it allow to do some workaround and make some very simple cast easy. like in this case :
<table [dataSource]="animals"> <=== Array<Animal>
<ng-container [cdkColumnDef]="columns.name.def">
<td class="align-items-center p-4 w-80" cdk-cell *cdkCellDef="let element">
*cdkCellDef="let element"
this IS an Animal
but angular template do not allow us to have the correct typing, so in this case let element | typeOf: Animal.DOG
should be completely safe
Upvotes: 1