発端: ApolloClient の useQuery の data と error をいい感じに扱うために getOrThrow(data, error, 'path.to.field')
みたいなユーティリティ関数が欲しくなった
欲しい関数
const {loading, error, data} = useQuery(query); const fooBar = getOrThrow(data, error, 'foo.bar'); const fooBarBaz = getOrThrow(data, error, 'foo.bar.baz');
こんな感じで、foo.bar でエラーが起きてたらエラーが throw され、エラーがなければ data.foo.bar が返ってくるという関数です。
実装:
さっそく実装を載せます(throw までつけると長くなるので、get に特化した実装を載せてます)。
ちなみに type-challenges の Object Key Paths の解答コードを借りました (こちらの解答)。
// cf. https://github.com/type-challenges/type-challenges/issues/7939 type ObjectKeyPaths< T extends object, P extends string = '', // prefix K extends keyof T = keyof T, > = K extends string ? | `${P}${K}` | (T[K] extends object ? `${P}${K}${ObjectKeyPaths<T[K], '.'>}` : never) : never; type Access<T extends object, U> = U extends `${infer x extends keyof T & string}.${infer xs}` ? T[x] extends object ? Access<T[x], xs> : T[x] : U extends keyof T ? T[U] : never; const access = <T extends object, U extends ObjectKeyPaths<T>>( object: T, path: U, ): Access<T, U> => path.split('.').reduce((acc, name) => (acc as any)[name], object) as any;
TypeScript: TS Playground - An online editor for exploring TypeScript and JavaScript
型を無理くりつけてるため実装に any を使ってます。これはもう仕方ないので、テストで保証すれば OK って方針にしました。