Wzorce
Jak odczytać `user.address.city` przez ścieżkę wielokrotnego użytku bez utraty typu wyniku?
Wygeneruj rekurencyjnie poprawne krotki kluczy, a potem zużywaj po jednym kluczu, by obliczyć wartość na końcu.
Przepis
type Paths<T> = T extends object ? { [K in keyof T]-?: [K] | [K, ...Paths<T[K]>] }[keyof T] : [] type PathValue<T, P extends readonly PropertyKey[]> = P extends readonly [infer K, ...infer Rest] ? K extends keyof T ? Rest extends readonly PropertyKey[] ? PathValue<T[K], Rest> : never : never : T function getPath<T, const P extends readonly PropertyKey[]>( value: T, ...path: P & Paths<T> ): PathValue<T, P> { let current: unknown = value for (const key of path) { current = (current as Record<PropertyKey, unknown>)[key] } return current as PathValue<T, P> } type Model = { user: { address: { city: string }; active: boolean } } declare const model: Model const city = getPath(model, 'user', 'address', 'city')
Build to kompiluje i sprawdza każdy wynik poniżej.
Jak to działa
- 01
type Paths<T> = T extends object
Typ mapowany emituje po jednej krotce dla każdej osiągalnej ścieżki właściwości.
- 02
PathValue<T[K], Rest>
Resolver podąża za pierwszym kluczem i rekurencyjnie przechodzi resztę krotki.
Co dostajesz
typeof city→stringPathValue<Model, ['user', 'active']>
→boolean['user', 'address', 'city'] extends Paths<Model> ? true : false
→true
Gdzie to zawodzi
Bardzo głębokie schematy mogą trafić na limit rekurencji kompilatora, a tablice ujawniają więcej kluczy, niż zwykle chce API ścieżek. Dla dużych modeli dodaj limit głębokości lub osobną obsługę tablic.
Wniosek
Przedstaw ścieżkę jako krotkę, gdy każdy segment zmienia typ następnego.
