Typescript 如何使用";从嵌套属性检索类型;“路径元组”;
考虑到在 但它只适用于根级别的路径,我担心我的typescript foo无法完成此任务。TS 4.1的更新 既然TypeScript支持和,您就可以更简单地编写Typescript 如何使用";从嵌套属性检索类型;“路径元组”;,typescript,Typescript,考虑到在 但它只适用于根级别的路径,我担心我的typescript foo无法完成此任务。TS 4.1的更新 既然TypeScript支持和,您就可以更简单地编写DeepIndex: type DeepIndex<T, KS extends Keys, Fail = undefined> = KS extends [infer F, ...infer R] ? F extends keyof T ? R extends Keys ? DeepIndex<T[F]
DeepIndex
:
type DeepIndex<T, KS extends Keys, Fail = undefined> =
KS extends [infer F, ...infer R] ? F extends keyof T ? R extends Keys ?
DeepIndex<T[F], R, Fail> : Fail : Fail : T;
我将DeepIndex
定义为接受类型T
和键类型的元组KS
,然后使用这些键进入T
,生成在那里找到的嵌套属性的类型。如果最终尝试使用它没有的键索引到某个对象,它将生成一个故障类型F
,该故障类型应默认为类似undefined
:
type Keys = readonly PropertyKey[];
type DeepIndex<T, KS extends Keys, F = undefined> = Idx0<T, KS, F>;
type Idx0<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx1<T[KS[0]], Tail<KS>, F> : F;
type Idx1<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx2<T[KS[0]], Tail<KS>, F> : F;
type Idx2<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx3<T[KS[0]], Tail<KS>, F> : F;
type Idx3<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx4<T[KS[0]], Tail<KS>, F> : F;
type Idx4<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx5<T[KS[0]], Tail<KS>, F> : F;
type Idx5<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx6<T[KS[0]], Tail<KS>, F> : F;
type Idx6<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx7<T[KS[0]], Tail<KS>, F> : F;
type Idx7<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx8<T[KS[0]], Tail<KS>, F> : F;
type Idx8<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx9<T[KS[0]], Tail<KS>, F> : F;
type Idx9<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? IdxX<T[KS[0]], Tail<KS>, F> : F;
type IdxX<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? T[KS[0]] : F;
因此,您可以看到deepIndex()
采用T
类型的obj
和KS
类型的键
,并应产生deepIndex
类型的结果。该实现使用键。reduce()
。让我们看看它是否有效:
const obj = {
a: { b: { c: 1 }, d: { e: "" } },
f: { g: { h: { i: true } } }, j: { k: [{ l: "hey" }] }
}
const c = deepIndex(obj, "a", "b", "c"); // number
const e = deepIndex(obj, "a", "d", "e"); // string
const i = deepIndex(obj, "f", "g", "h", "i"); // boolean
const l = deepIndex(obj, "j", "k", 0, "l"); // string
const oops = deepIndex(obj, "a", "b", "c", "d"); // undefined
const hmm = deepIndex(obj, "a", "b", "c", "toFixed"); // (fractionDigits?: number) => string
我觉得不错
请注意,我确信您希望使用
deepIndex()
函数或deepIndex
类型将KS
类型约束为来自路径的类型,而不是输出未定义的
。我尝试了五种不同的方法来实现这一点,其中大多数都彻底破坏了编译器。那些没有破坏编译器的代码比上面的代码更难看、更复杂,对于一个新手来说,它们确实没有给出有用的错误信息;我不久前提出的一个bug导致错误出现在keys
数组的错误元素上。所以你想看看
const oops = deepIndex(obj, "a", "b", "c", "d"); // error!
// --------------------------------------> ~~~
// "d" is not assignable to keyof number
但实际发生的是
const oops = deepIndex(obj, "a", "b", "c", "d"); // error!
// -----------------------> ~~~
// "d" is not assignable to never
所以我放弃了。如果你有胆量的话,你可以继续努力。整个努力真的把事情推到了一个我不愿意让别人去做的程度。我认为这是“编译器的有趣和令人兴奋的挑战”,而不是“任何人的生计都应该依赖的代码”
好吧,希望这会有帮助;祝你好运
感谢您花时间如此详细地访问anwser。你对TypeScript编译器的见解非常有价值,也很容易理解,你可以在博客上发表。
type Tail<T> = T extends readonly any[] ?
((...t: T) => void) extends ((h: any, ...r: infer R) => void) ? R : never
: never;
type Keys = readonly PropertyKey[];
type DeepIndex<T, KS extends Keys, F = undefined> = Idx0<T, KS, F>;
type Idx0<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx1<T[KS[0]], Tail<KS>, F> : F;
type Idx1<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx2<T[KS[0]], Tail<KS>, F> : F;
type Idx2<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx3<T[KS[0]], Tail<KS>, F> : F;
type Idx3<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx4<T[KS[0]], Tail<KS>, F> : F;
type Idx4<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx5<T[KS[0]], Tail<KS>, F> : F;
type Idx5<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx6<T[KS[0]], Tail<KS>, F> : F;
type Idx6<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx7<T[KS[0]], Tail<KS>, F> : F;
type Idx7<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx8<T[KS[0]], Tail<KS>, F> : F;
type Idx8<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx9<T[KS[0]], Tail<KS>, F> : F;
type Idx9<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? IdxX<T[KS[0]], Tail<KS>, F> : F;
type IdxX<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? T[KS[0]] : F;
function deepIndex<T, KS extends Keys, K extends PropertyKey>(
obj: T,
...keys: KS & K[]
): DeepIndex<T, KS>;
function deepIndex(obj: any, ...keys: Keys) {
return keys.reduce((o, k) => o?.[k], obj);
}
const obj = {
a: { b: { c: 1 }, d: { e: "" } },
f: { g: { h: { i: true } } }, j: { k: [{ l: "hey" }] }
}
const c = deepIndex(obj, "a", "b", "c"); // number
const e = deepIndex(obj, "a", "d", "e"); // string
const i = deepIndex(obj, "f", "g", "h", "i"); // boolean
const l = deepIndex(obj, "j", "k", 0, "l"); // string
const oops = deepIndex(obj, "a", "b", "c", "d"); // undefined
const hmm = deepIndex(obj, "a", "b", "c", "toFixed"); // (fractionDigits?: number) => string
const oops = deepIndex(obj, "a", "b", "c", "d"); // error!
// --------------------------------------> ~~~
// "d" is not assignable to keyof number
const oops = deepIndex(obj, "a", "b", "c", "d"); // error!
// -----------------------> ~~~
// "d" is not assignable to never