Migliore sicurezza di tipo con componenti generici angolari
Migliora la sicurezza del tipo dei tuoi componenti senza sacrificare la flessibilità
Di recente, avevo bisogno di creare un componente a discesa in Angular per applicare alcuni stili e comportamenti personalizzati. Questo è iniziato piuttosto semplice. Ho fatto qualcosa di simile al seguente:
// dropdown.component.ts
@Component({
selector: 'app-dropdown',
templateUrl: './dropdown.component.html'
})
export class DropdownComponent {
@Input()
currentOption: any; // <- Yikes!
@Input()
options: any[]; // <- Yikes!
}
Ho pensato di usare unknowninvece di any, ma questo mi ha dato più problemi, e mi è anche sembrato un "meglio any". Voglio che i miei tipi dang it!
Bene, si scopre che puoi effettivamente fare in modo che il tuo componente abbia un tipo generico! Angular è abbastanza intelligente da sapere di che tipo è il tuo componente dall'uso. Ecco la sua applicazione:
// dropdown.component.ts
@Component({
selector: 'app-dropdown',
templateUrl: './dropdown.component.html'
})
export class DropdownComponent<T> {
@Input()
currentOption: T;
@Input()
options: T[];
}
// app.component.ts
type MyCustomType = { name: string, value: number };
type SomeOtherType = { title: string, value: string };
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
currentOption: MyCustomType = { name: 'foo', value: 42};
// Intentionally using wrong type
options: SomeOtherType = [
{
title: 'foo',
value: '42',
},
{
title: 'bar',
value: '13',
},
]
}
<!-- app.component.html -->
<app-dropdown
[value]="currentOption"
[options]="options" <!-- Error: not assignable to MyCustomType -->
>
</app-dropdown>
Prova a passarci sopra <app-dropdown>e guarda cosa ti dice intellisense sul tuo componente.
Chiamami masochista, ma mi piace quando il dattiloscritto mi urla contro quando commetto un errore. Meglio delle voci nella mia testa che innescano la mia sindrome dell'impostore quando ricevo un errore di runtime!
Un caso d'uso pratico
Il componente a discesa deve ancora mostrare le sue opzioni. Il modello sarebbe simile al seguente:
<!-- dropdown.component.html -->
<select>
<option *ngFor="let option of options">
{{ option.whatGoesHere? }} <!-- hmmmm.... -->
</option>
</select>
Per soddisfare le esigenze del nostro componente a discesa, possiamo creare un tipo come il seguente:
// dropdown.component.ts
export type DropdownOption<T> = {
text: string;
value: T;
}
@Component({
selector: 'app-dropdown',
templateUrl: './dropdown.component.html'
})
export class DropdownComponent<T> {
// ...
// dropdown.component.ts
@Component({
selector: 'app-dropdown',
templateUrl: './dropdown.component.html'
})
export class DropdownComponent<T> {
@Input()
value: DropdownOption<T>;
@Input()
options: DropdownOption<T>[];
}
<!-- dropdown.component.html -->
<select>
<option *ngFor="let option of options">
{{ option.text }}
</option>
</select>
Tornando a dove abbiamo usato il nostro currentOptione availableOptions, ora otteniamo il seguente errore:
<!-- app.component.html -->
<app-dropdown
[value]="currentOption" <!-- Error: not assignable to DropdownOption -->
[options]="options" <!-- Error: not assignable to DropdownOption[] -->
>
</app-dropdown>
type MyCustomType = { name: string, value: number };
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
currentOption: MyCustomType = { name: 'foo', value: 42};
options: MyCustomType[] = [
{
name: 'foo',
value: 42,
},
{
name: 'bar',
value: 13,
},
]
// ## NEW ##
get valueAsDropdownItem(): DropdownOption<MyCustomType> {
return this.toDropdownOption(this.currentOption);
}
// ## NEW ##
get optionsAsDropdownItems(): DropdownOption<MyCustomType>[] {
return this.options.map(option => this.toDropdownOption(option));
}
// ## NEW ##
toDropdownOption(item: MyCustomType): DropdownOption<MyCustomType> {
return {
title: item.name,
value: item,
}
}
}
<!-- app.component.html -->
<app-dropdown
[value]="valueAsDropdownItem"
[options]="optionsAsDropdownItems"
>
</app-dropdown>
Riferimenti
- Articolo sui componenti generici da Damir's Corner ( link )
- Repository con implementazione finale

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































