Aggiungi ? dopo il nome di un parametro per renderlo opzionale. Chi chiama può ometterlo, e dentro la funzione il suo tipo include undefined.
TypeScript controlla il numero di argomenti, quindi senza il ? la prima chiamata sarebbe un errore di compilazione: Expected 2 arguments, but got 1. (TS2554).
I parametri opzionali possono essere undefined
Dato che chi chiama può ometterlo, il tipo di un parametro opzionale dentro la funzione è T | undefined. Con strictNullChecks devi gestire il caso undefined prima di usarlo come T.
L'optional chaining (?.) e il nullish coalescing (??) sono gli strumenti abituali in questi casi. Quando il valore di ripiego è fisso, un parametro predefinito è più breve.
Valori predefiniti dei parametri
Un valore predefinito rende il parametro opzionale per chi chiama e gli dà un tipo senza undefined dentro la funzione. Il tipo viene inferito dal valore predefinito, quindi spesso l'annotazione non serve.
Chi chiama vede la firma repeat(text: string, times?: number, separator?: string). I valori predefiniti seguono la regola di JavaScript: si applicano quando l'argomento è undefined, sia omesso sia passato in modo esplicito, e non quando è null. Un'espressione predefinita può usare i parametri precedenti: function range(start: number, end = start + 10).
Regole sull'ordine dei parametri
| Dichiarazione | Compila? | Note |
|---|---|---|
(a: number, b?: number) | Sì | I parametri opzionali vanno in fondo |
(a?: number, b: number) | No | TS1016: A required parameter cannot follow an optional parameter. |
(a = 0, b: number) | Sì | Ma chi chiama deve scrivere f(undefined, 5) per usare il valore predefinito |
(a: number, ...rest: number[]) | Sì | Un parametro rest è sempre l'ultimo |
(a?: number, ...rest: number[]) | Sì | Un opzionale prima del rest è ammesso |
Un parametro con valore predefinito prima di uno obbligatorio è lecito ma scomodo. Per chi chiama il suo tipo diventa number | undefined, e a nessuno piace scrivere undefined come segnaposto. Se ti serve un primo parametro flessibile, usa invece un oggetto di opzioni o l'overloading delle funzioni.
Omesso vs undefined
x?: number e x: number | undefined si somigliano e hanno lo stesso tipo dentro la funzione. Cambiano per chi chiama: il primo si può omettere, il secondo va passato.
Usa | undefined per un argomento obbligatorio che può non avere un valore, così ogni chiamante deve pensarci. Usa ? quando ometterlo è una chiamata normale. (Il messaggio dice davvero "1 arguments": è la formulazione di TypeScript.)
Parametri rest
Un parametro rest, ...name: T[], raccoglie un numero qualsiasi di argomenti in un array. Deve essere l'ultimo parametro.
Espandere un array in parametri fissi è più rigido. Un parametro rest accetta lo spread di qualsiasi number[], ma una funzione dichiarata (a: number, b: number) accetta solo lo spread di una tupla, perché TypeScript deve conoscerne la lunghezza:
function point(x: number, y: number) { return { x, y }; }
const list = [3, 4]; // number[]
point(...list);
// error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
const pair = [3, 4] as const; // readonly [3, 4]
point(...pair); // fine
Anche un parametro rest può avere un tipo tupla, che tipizza ogni posizione: ...args: [name: string, age?: number].
Oggetti di opzioni
Quando una funzione ha più di due o tre parametri opzionali, chi chiama perde il conto delle posizioni. Un oggetto di opzioni con valori predefiniti dà argomenti con nome e senza ordine.
Il = {} finale rende opzionale l'intero oggetto. Senza, fetchData("/a") è un errore di compilazione (Expected 2 arguments, but got 1., TS2554), e in JavaScript puro la stessa chiamata lancerebbe un TypeError in fase di esecuzione, perché la destrutturazione ha bisogno di un oggetto da cui leggere.
Parametri opzionali nei tipi di callback
In un tipo di funzione, ? significa "chi chiama questa callback potrebbe non passarlo". Non significa "la callback può ignorarlo": una callback può sempre ignorare i parametri finali. Quindi non segnare come opzionali i parametri di una callback solo per permettere agli handler di ricevere meno argomenti.
// Too loose: every handler must now cope with index being undefined
type Visit = (item: string, index?: number) => void;
// Right: the caller always passes both; handlers may use only item
type VisitStrict = (item: string, index: number) => void;
const log: VisitStrict = (item) => console.log(item);
Domande frequenti
Come si rende opzionale un parametro in TypeScript?
Metti un ? dopo il suo nome: function greet(name?: string). Chi chiama può ometterlo, e dentro la funzione il suo tipo è string | undefined, quindi lo controlli prima di usarlo. Dare al parametro un valore predefinito, name = "there", lo rende anch'esso opzionale ed elimina l'undefined dentro la funzione.
Un parametro opzionale può venire prima di uno obbligatorio in TypeScript?
Non con ?: (a?: number, b: number) è l'errore TS1016, "A required parameter cannot follow an optional parameter." Un parametro con valore predefinito può venire prima, ma allora chi chiama deve passare undefined in modo esplicito per usare il valore predefinito, quindi in pratica i parametri opzionali e quelli con valore predefinito vanno in fondo.
Che differenza c'è tra x?: number e x: number | undefined?
Dentro la funzione sono entrambi number | undefined. La differenza sta nella chiamata: con x?: number l'argomento si può omettere, mentre con x: number | undefined va passato, anche se il valore è undefined. Ometterlo dà l'errore TS2554.
Passare null usa il valore predefinito del parametro?
No. JavaScript applica un valore predefinito solo quando l'argomento è undefined (omesso o passato in modo esplicito). null è un valore, quindi viene mantenuto. In ogni caso TypeScript rifiuta null per un parametro number con strictNullChecks.
Come passo un array come argomenti separati in TypeScript?
Espandilo con lo spread: fn(...args). Per una funzione con parametri fissi, l'array deve avere un tipo tupla come [number, number] o venire da as const; espandere un number[] dà l'errore TS2556 perché la sua lunghezza è sconosciuta. Espandere in un parametro rest (...values: number[]) funziona sempre.