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ámetro | Tipo | Descripción | Requerido |
|---|---|---|---|
obj | any | Objeto del cual extraer la propiedad | ✅ |
path | string[] | null | undefined | Array 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.
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).
obj es null o undefinedCon 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)
Navegación de Respuestas de API
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
undefineden cuanto encuentra un eslabón falso (inexistente,null,0,'',false,NaN) - Errores: Solo lanza
TypeErrorcuandoobjesnull/undefinedy el path no está vacío - Dependencia: Usa
hasValuepara decidir si el path trae valor - Cache friendly: Cachea el valor actual en cada iteración
- Type-agnostic: Retorna
anypara 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
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'
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