Sugerencias
← TIL
~2 min de lectura
#typescript#dx#javascript

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.

Acceso a propiedades anidadas
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 | undefined
city
= 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:

Optional call
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 ??:

Terminal
interface Settings {
  
Settings.theme?: {
    primary?: string;
} | undefined
theme
?: {
primary?: string | undefinedprimary?: string; }; } declare const const settings: Settingssettings: Settings; const
const primaryColor: string
primaryColor
= 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á.


Referencias

Enlace copiado al portapapeles