Saltar al contenido principal

getPath

Paquete: @tiendana/utils

Accede de forma segura a propiedades profundamente anidadas de un objeto mediante un array de strings que representa la ruta. Retorna undefined si alguna propiedad de la ruta no existe o tiene un valor falso (0, '', false, null, NaN).

Características​

✅ Acceso seguro a propiedades anidadas
✅ No lanza errores si la ruta no existe (salvo que obj sea null/undefined)
✅ Acepta paths como array de strings
✅ Maneja null y undefined en path
✅ Más limpio que optional chaining múltiple
✅ Útil para acceso dinámico a propiedades

Firma​

function getPath(
obj: any,
path: string[] | null | undefined
): any

Parámetros​

ParámetroTipoDescripciónRequerido
objanyObjeto del cual extraer la propiedad✅
pathstring[] | null | undefinedArray de strings representando la ruta a la propiedad❌

Retorno​

Tipo: any

El valor en la ruta especificada. Retorna undefined si algún eslabón de la ruta, incluido el valor final, es falso (undefined, null, 0, '', false, NaN). Con path vacío, null o undefined retorna obj tal cual.

Los valores falsos se pierden

Internamente cada paso hace if (!current[key]) return undefined, así que un valor final 0, '' o false se pierde y llega como undefined:

getPath({ a: 0 }, ['a']);              // undefined
getPath({ a: { b: false } }, ['a', 'b']); // undefined
getPath({ a: '' }, ['a']); // undefined

Si necesitas distinguir 0, '' o false, usa optional chaining (obj?.a?.b).

Lanza error si obj es null o undefined

Con un path no vacío, getPath(null, ['a']) lanza TypeError, porque lee la propiedad del objeto raíz sin comprobarlo. Con path vacío o nulo retorna obj sin error.

Ejemplos de Uso​

Acceso Básico​

import { getPath } from '@tiendana/utils';

const user = {
name: 'Juan',
address: {
city: 'Madrid',
country: 'España'
}
};

getPath(user, ['name']);
// 'Juan'

getPath(user, ['address', 'city']);
// 'Madrid'

getPath(user, ['address', 'zipCode']);
// undefined (no existe)

Comparación con Optional Chaining​

const user = {
profile: {
contact: {
email: 'juan@example.com'
}
}
};

// Con optional chaining
const email1 = user?.profile?.contact?.email;

// Con getPath
const email2 = getPath(user, ['profile', 'contact', 'email']);

// Ambos retornan: 'juan@example.com'

Acceso Dinámico​

interface Config {
api: {
endpoints: {
users: string;
products: string;
orders: string;
}
}
}

function getEndpoint(config: Config, resource: string): string | undefined {
return getPath(config, ['api', 'endpoints', resource]);
}

getEndpoint(config, 'users');
// 'https://api.example.com/users'

getEndpoint(config, 'payments');
// undefined (no existe)
interface ApiResponse {
data: {
user: {
profile: {
name: string;
avatar: string;
};
settings: {
theme: string;
notifications: boolean;
};
};
};
meta: {
timestamp: number;
};
}

function getUserName(response: ApiResponse): string | undefined {
return getPath(response, ['data', 'user', 'profile', 'name']);
}

function getUserTheme(response: ApiResponse): string | undefined {
return getPath(response, ['data', 'user', 'settings', 'theme']);
}

Extracción de Datos Anidados​

const product = {
details: {
pricing: {
base: 100,
discount: {
percentage: 20,
validUntil: '2024-12-31'
}
}
}
};

const discount = getPath(product, ['details', 'pricing', 'discount', 'percentage']);
// 20

const tax = getPath(product, ['details', 'pricing', 'tax']);
// undefined

Configuración de Aplicación​

interface AppConfig {
features: {
authentication: {
providers: {
google: { enabled: boolean; clientId: string };
facebook: { enabled: boolean; appId: string };
};
};
};
}

function isProviderEnabled(
config: AppConfig,
provider: 'google' | 'facebook'
): boolean {
return getPath(config, [
'features',
'authentication',
'providers',
provider,
'enabled'
]) || false;
}

isProviderEnabled(config, 'google');
// true/false

Manejo de Path Null/Undefined​

const user = { name: 'Juan', age: 30 };

getPath(user, null);
// { name: 'Juan', age: 30 } ← Retorna el objeto completo

getPath(user, undefined);
// { name: 'Juan', age: 30 } ← Retorna el objeto completo

getPath(user, []);
// { name: 'Juan', age: 30 } ← Array vacío retorna objeto completo

Valores por Defecto​

function getConfigValue<T>(
config: any,
path: string[],
defaultValue: T
): T {
const value = getPath(config, path);
return value !== undefined ? value : defaultValue;
}

const timeout = getConfigValue(config, ['api', 'timeout'], 5000);
// 5000 si no existe config.api.timeout
// (también 5000 si timeout es 0, porque getPath devuelve undefined para valores falsos)

Acceso a Arrays​

const data = {
users: [
{ name: 'Juan', age: 30 },
{ name: 'María', age: 25 }
]
};

getPath(data, ['users', '0', 'name']);
// 'Juan'

getPath(data, ['users', '1', 'age']);
// 25

getPath(data, ['users', '2', 'name']);
// undefined (índice no existe)

Validación de Estructura​

function hasRequiredFields(obj: any, requiredPaths: string[][]): boolean {
return requiredPaths.every(path => {
const value = getPath(obj, path);
return value !== undefined; // un valor 0, '' o false también cuenta como ausente
});
}

const isValid = hasRequiredFields(apiResponse, [
['data', 'user', 'id'],
['data', 'user', 'email'],
['meta', 'timestamp']
]);
// true si todas las rutas existen

Casos de Uso Comunes​

  • API responses: Navegar respuestas de API con estructura compleja
  • Configuración: Acceder a valores de config anidados
  • Validación: Verificar existencia de propiedades anidadas
  • Acceso dinámico: Acceder a propiedades usando rutas construidas dinámicamente
  • Datos externos: Trabajar con datos de terceros con estructura variable
  • Migraciones: Acceder a estructuras que pueden variar entre versiones
  • Formularios: Acceder a valores de formularios multi-nivel

Comportamiento con Valores Especiales​

Path Vacío o Null​

const obj = { name: 'Juan' };

getPath(obj, null); // { name: 'Juan' }
getPath(obj, undefined); // { name: 'Juan' }
getPath(obj, []); // { name: 'Juan' }

Propiedad No Existe​

const obj = { a: { b: 'value' } };

getPath(obj, ['a', 'b']); // 'value'
getPath(obj, ['a', 'c']); // undefined
getPath(obj, ['x', 'y', 'z']); // undefined

Valor Falso en la Ruta​

const obj = {
a: {
b: null
}
};

getPath(obj, ['a', 'b']); // undefined (null es un valor falso)
getPath(obj, ['a', 'b', 'c']); // undefined (no puede acceder c de null)

Notas Técnicas​

  • Performance: O(n) donde n es la longitud del path
  • Loop-based: Usa un loop for para iterar sobre el path
  • Early return: Retorna undefined en cuanto encuentra un eslabón falso (inexistente, null, 0, '', false, NaN)
  • Errores: Solo lanza TypeError cuando obj es null/undefined y el path no está vacío
  • Dependencia: Usa hasValue para decidir si el path trae valor
  • Cache friendly: Cachea el valor actual en cada iteración
  • Type-agnostic: Retorna any para máxima flexibilidad

Ventajas sobre Optional Chaining​

Rutas Dinámicas​

// ❌ No puedes construir optional chaining dinámicamente
const path = ['user', 'profile', 'name'];
// user?.profile?.name ← No puedes hacerlo con variable

// ✅ getPath acepta rutas dinámicas
getPath(user, path);

Rutas Reutilizables​

const USER_NAME_PATH = ['data', 'user', 'profile', 'name'];
const USER_EMAIL_PATH = ['data', 'user', 'profile', 'email'];

function extractUserData(response: any) {
return {
name: getPath(response, USER_NAME_PATH),
email: getPath(response, USER_EMAIL_PATH)
};
}

Validación de Múltiples Rutas​

const REQUIRED_PATHS = [
['config', 'api', 'url'],
['config', 'api', 'key'],
['config', 'features', 'enabled']
];

const allExist = REQUIRED_PATHS.every(path =>
getPath(config, path) !== undefined
);

Limitaciones​

Índices de array

El tipo string[] rechaza números, pero en ejecución un índice numérico funciona igual. Escribe los índices como strings (['items', '0']) para cumplir el tipo:

const data = { items: ['a', 'b', 'c'] };

getPath(data, ['items', '0']); // 'a'
No distingue entre ausente y falso

La función retorna undefined tanto si la propiedad no existe como si su valor es undefined o cualquier otro valor falso:

const obj = {
a: undefined,
b: 'value'
};

getPath(obj, ['a']); // undefined (existe pero es undefined)
getPath(obj, ['c']); // undefined (no existe)

// No hay forma de diferenciar ambos casos

Ver También​