Optional Chaining shorthand en TypeScript
Tenés un objeto que llega de una API externa. Algunas propiedades son opcionales; otras dependen de si el usuario está autenticado. El if anidado para acceder a user.profile.address.city sin que explote es un clásico feo.
?. resuelve eso. Si cualquier parte de la cadena es null o undefined, TypeScript cortocircuita y devuelve undefined en lugar de lanzar un TypeError.
interface User {
User.profile?: {
address?: {
city?: string;
};
} | undefined
profile?: {
address?: {
city?: string;
} | undefined
address?: {
city?: string | undefinedcity?: string;
};
};
}
declare const const user: User | nulluser: User | null;
// Sin optional chaining: TypeError en runtime si user es null
// const city = user.profile.address.city;
// Con optional chaining: undefined seguro
const const city: string | undefinedcity = const user: User | nulluser?.User.profile?: {
address?: {
city?: string;
};
} | undefined
profile?.address?: {
city?: string;
} | undefined
address?.city?: string | undefinedcity;
El caso que menos se muestra: optional call ?.()
Acceder a propiedades es el ejemplo de manual. El que aparece menos en tutoriales es la llamada a métodos opcionales con ?.(). Si trabajás con callbacks o funciones que pueden venir como undefined desde props o configs:
type type Config = {
onSuccess?: (data: string) => void;
}
Config = {
onSuccess?: ((data: string) => void) | undefinedonSuccess?: (data: stringdata: string) => void;
};
function function processData(config: Config, data: string): voidprocessData(config: Configconfig: type Config = {
onSuccess?: (data: string) => void;
}
Config, data: stringdata: string) {
// Sin optional call: if (config.onSuccess) config.onSuccess(data)
config: Configconfig.onSuccess?: ((data: string) => void) | undefinedonSuccess?.(data: stringdata);
}El compilador infiere correctamente que onSuccess puede no existir y no requiere el guard manual.
Combinado con nullish coalescing (??)
?. solo devuelve undefined. Para definir un valor por defecto en la misma línea, se combina con ??:
interface Settings {
Settings.theme?: {
primary?: string;
} | undefined
theme?: {
primary?: string | undefinedprimary?: string;
};
}
declare const const settings: Settingssettings: Settings;
const const primaryColor: stringprimaryColor = const settings: Settingssettings.Settings.theme?: {
primary?: string;
} | undefined
theme?.primary?: string | undefinedprimary ?? "#3b82f6";
El tipo resultante es string (no string | undefined) porque ?? garantiza el fallback. TypeScript lo infiere solo; no hace falta cast ni aserción de tipo.
Qué no hace
?. protege contra null y undefined. No protege contra propiedades que existen pero tienen un valor falsy como 0, "" o false. Si user.age es 0, user?.age ?? 18 devuelve 0, no 18. Eso es correcto; el operador || es el que tendría el comportamiento incorrecto acá.