Комментарии 5
В общем случае для таких штук удобнее не юнион вообще всех ключей сразу собирать, а постепенно выдавать подсказки по уровням вложенности (как в иде). Это решает проблемы со слишком большими юнионами, вычисляемыми ключами и массивами.
Чтобы примерно так работало:
const data = { foo: { a: string, b: number }, bar: string }
validatePath(data, "")
// ^ 'foo' | 'bar'
validatePath(data, "foo.")
// ^ 'foo.a' | 'foo.b'
Чорт возьми, это можно сделать! У меня получилась очень странная конструкция (можно ли проще?), которая работает в TS 5.2+
Набросок (без фильтров по массивам и т.д., просто сама идея)
type GetKeys<
O extends object,
K extends string,
P extends string = '',
> = K extends `${infer I}.${infer X}`
? I extends keyof O
? O[I] extends object
? GetKeys<O[I], X, `${P}${I}.`>
: never
: never
: `${P}${string & keyof O}`;
declare const validatePath: <
O extends object,
P extends string,
P2 extends GetKeys<O, P>
>(ob: O, p: (P | P2) & GetKeys<O, P2>) => P;
// ---- пример использования -------
const obj = {
aaa: {
aa: {
qq: 1,
qq1: {
preved: 2,
},
ww: 3,
},
bb: {
zz: 4,
xx: {
tt: 5,
},
},
},
abb: {
m: 6
}
};
const r = validatePath(obj, ''); // печатать здесь
Воистину нет более черной магии, чем вывод типов в тайпскрипте..
Можно например так сделать (не знаю насколько проще, но имхо понятнее):
declare const validatePath: <
O extends object,
P extends string,
>(ob: O, p: P extends GetKeys<O, P> ? P : GetKeys<O, P>) => P;
Звучит интересно, но итоговый код использую несколько месяцев и пока что не возникало такой необходимости. Думаю, когда столкнулись, переделаю под новые требования.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Типизация свойства объекта в виде строки