Añade ? después del nombre de un parámetro para hacerlo opcional. Quien llama puede omitirlo, y dentro de la función su tipo incluye undefined.
TypeScript comprueba el número de argumentos, así que sin el ? la primera llamada sería un error de compilación: Expected 2 arguments, but got 1. (TS2554).
Los parámetros opcionales pueden ser undefined
Como quien llama puede omitirlo, dentro de la función el tipo de un parámetro opcional es T | undefined. Con strictNullChecks debes manejar el caso undefined antes de usarlo como T.
El encadenamiento opcional (?.) y el operador de fusión nula (??) son las herramientas habituales aquí. Cuando el valor alternativo es fijo, un parámetro con valor por defecto es más corto.
Valores por defecto de los parámetros
Un valor por defecto hace que el parámetro sea opcional para quien llama y le da un tipo sin undefined dentro de la función. El tipo se infiere a partir del valor por defecto, así que la anotación a menudo sobra.
Quien llama ve la firma repeat(text: string, times?: number, separator?: string). Los valores por defecto siguen la regla de JavaScript: se aplican cuando el argumento es undefined, ya sea omitido o pasado de forma explícita, y no cuando es null. Una expresión por defecto puede usar parámetros anteriores: function range(start: number, end = start + 10).
Reglas de orden de los parámetros
| Declaración | ¿Compila? | Notas |
|---|---|---|
(a: number, b?: number) | Sí | Los parámetros opcionales van al final |
(a?: number, b: number) | No | TS1016: A required parameter cannot follow an optional parameter. |
(a = 0, b: number) | Sí | Pero quien llama debe escribir f(undefined, 5) para usar el valor por defecto |
(a: number, ...rest: number[]) | Sí | Un parámetro rest siempre va al final |
(a?: number, ...rest: number[]) | Sí | Se permite un opcional antes del rest |
Un parámetro con valor por defecto antes de uno obligatorio es válido pero incómodo. Para quien llama su tipo pasa a ser number | undefined, y a nadie le gusta escribir undefined como relleno. Si necesitas un primer parámetro flexible, usa un objeto de opciones o la sobrecarga de funciones.
Omitido frente a undefined
x?: number y x: number | undefined se parecen y tienen el mismo tipo dentro de la función. Se diferencian para quien llama: el primero se puede omitir, el segundo hay que pasarlo.
Usa | undefined para un argumento obligatorio que puede no tener valor, de modo que cada llamada tenga que pensar en ello. Usa ? cuando omitirlo sea una llamada normal. (El mensaje dice de verdad "1 arguments": es la redacción de TypeScript).
Parámetros rest
Un parámetro rest, ...name: T[], recoge cualquier número de argumentos en un array. Tiene que ser el último parámetro.
Expandir un array en parámetros fijos es más estricto. Un parámetro rest acepta la expansión de cualquier number[], pero una función declarada (a: number, b: number) solo acepta la expansión de una tupla, porque TypeScript necesita conocer la longitud:
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
Un parámetro rest también puede tener un tipo tupla, que tipa cada posición: ...args: [name: string, age?: number].
Objetos de opciones
Cuando una función tiene más de dos o tres parámetros opcionales, quien llama pierde la cuenta de las posiciones. Un objeto de opciones con valores por defecto da argumentos con nombre y sin orden.
El = {} del final hace opcional el objeto entero. Sin él, fetchData("/a") es un error de compilación (Expected 2 arguments, but got 1., TS2554), y en JavaScript normal la misma llamada lanzaría un TypeError en tiempo de ejecución, porque la desestructuración necesita un objeto del que leer.
Parámetros opcionales en tipos de callback
En un tipo de función, ? significa «quien llama a este callback puede no pasarlo». No significa «el callback puede ignorarlo»: un callback siempre puede ignorar los últimos parámetros. Así que no marques como opcionales los parámetros de un callback solo para que los manejadores puedan recibir menos argumentos.
// 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);
Preguntas frecuentes
¿Cómo se hace opcional un parámetro en TypeScript?
Pon un ? después de su nombre: function greet(name?: string). Quien llama puede omitirlo, y dentro de la función su tipo es string | undefined, así que lo compruebas antes de usarlo. Dar al parámetro un valor por defecto, name = "there", también lo hace opcional y elimina el undefined dentro de la función.
¿Puede un parámetro opcional ir antes que uno obligatorio en TypeScript?
No con ?: (a?: number, b: number) es el error TS1016, "A required parameter cannot follow an optional parameter." Un parámetro con valor por defecto puede ir primero, pero entonces quien llama tiene que pasar undefined de forma explícita para usar el valor por defecto, así que en la práctica los parámetros opcionales y con valor por defecto van al final.
¿Qué diferencia hay entre x?: number y x: number | undefined?
Dentro de la función los dos son number | undefined. La diferencia está en la llamada: con x?: number el argumento se puede omitir, mientras que con x: number | undefined hay que pasarlo, aunque el valor sea undefined. Omitirlo da el error TS2554.
¿Pasar null usa el valor por defecto del parámetro?
No. JavaScript aplica un valor por defecto solo cuando el argumento es undefined (omitido o pasado de forma explícita). null es un valor, así que se conserva. De todos modos, TypeScript rechaza null para un parámetro number con strictNullChecks.
¿Cómo paso un array como argumentos separados en TypeScript?
Expándelo: fn(...args). En una función con parámetros fijos, el array debe tener un tipo tupla como [number, number] o venir de as const; expandir un number[] da el error TS2556 porque su longitud es desconocida. Expandir en un parámetro rest (...values: number[]) siempre funciona.