TypeScript è diventato lo standard industriale per lo sviluppo web su larga scala. Mentre la maggior parte degli sviluppatori conosce le basi delle interfacce e dei tipi, il vero potere risiede nel suo sistema di tipi avanzato. Ecco 5 pattern che ti distingueranno come senior engineer.
1. Generic Constraints
I generici sono potenti, ma a volte è necessario limitare ciò che può essere passato. extends è il tuo amico qui.
interface HasId { id: string; } function getById<T extends HasId>(list: T[], id: string): T | undefined { return list.find((item) => item.id === id); }
Assicurando che T estenda HasId, garantiamo che l'accesso a .id all'interno della funzione sia sicuro.
2. Conditional Types
I tipi condizionali permettono di creare mappature di tipi non uniformi. La sintassi è simile all'operatore ternario in JavaScript.
type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false
Un caso d'uso pratico è filtrare i tipi da un'unione:
type Diff<T, U> = T extends U ? never : T; type NonNullable<T> = Diff<T, null | undefined>;
3. Mapped Types
I tipi mappati permettono di creare nuovi tipi basati su quelli vecchi trasformando le proprietà.
type ReadOnly<T> = { readonly [P in keyof T]: T[P]; }; interface User { name: string; age: number; } type ReadOnlyUser = ReadOnly<User>;
Puoi anche aggiungere o rimuovere modificatori:
type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
4. Template Literal Types
Introdotti in TypeScript 4.1, questi permettono di manipolare direttamente i tipi stringa.
type World = 'world'; type Greeting = `hello ${World}`; // "hello world" type Color = 'red' | 'blue'; type Quantity = 'light' | 'dark'; type Palette = `${Quantity}-${Color}`; // "light-red" | "light-blue" | "dark-red" | "dark-blue"
Questo è incredibilmente utile per tipizzare stringhe che seguono un pattern specifico, come classi CSS o nomi di eventi.
5. La Parola Chiave infer
La parola chiave infer all'interno dei tipi condizionali permette di estrarre tipi da altri tipi.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function check(): boolean { return true; } type CheckReturn = ReturnType<typeof check>; // boolean
Qui, stiamo chiedendo a TypeScript di "inferire" il tipo di ritorno R di una funzione e restituirlo.
Conclusione
Padroneggiare questi pattern ti permette di scrivere librerie e utility robuste e che forniscono un'eccellente esperienza di sviluppo (DX). L'obiettivo di TypeScript avanzato non è la complessità per il gusto della complessità, ma la sicurezza e l'espressività.