Issue
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,
- I do not use the ngModel like this
[(value)]="country"
and I make a method that typecheck in all my components that use a select: - I create a type for every type of value my select use and cast to it.
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 ?
Solution
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
Answered By - Crocsx
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.