TypeScript:获取嵌套对象键的联合类型
我想提取嵌套对象的键类型,并尝试了类似下面的做法。
TS 演示链接
type RecursiveRecord = {
[key in string]: string | RecursiveRecord;
};
type Keys<T extends RecursiveRecord, K = keyof T> = K extends string
? T[K] extends string
? K
: T[K] extends RecursiveRecord
? K | Keys<T[K]> // here I got error
: never
: never;
type Obj = {
a: {
c: 'aaaaaa';
d: 'aaaaaaaaaaa';
e: { f: 'q' };
};
b: 'dd';
};
export type A = Keys<Obj>; // want to get "a" | "b" | "c" | "d" | "e" | "f"
但是在 K | Keys<T[K]> 上,我得到了以下类型错误。有没有什么干净的方法来解决这个问题?
index.ts:9:16 - error TS2344: Type 'T[K]' does not satisfy the constraint 'RecursiveRecord'.
Type 'T[string]' is not assignable to type 'RecursiveRecord'.
Type 'string | RecursiveRecord' is not assignable to type 'RecursiveRecord'.
Type 'string' is not assignable to type 'RecursiveRecord'.
答案#1
在 TypeScript 的 GitHub 仓库里至少有一个关于这个问题的未解决议题:请参见 microsoft/TypeScript#25804,该议题建议允许条件类型跟踪更复杂的检查类型(如 T[K])的约束。目前这个议题只是被标记为“等待更多反馈”,所以如果我们希望看到它有所进展,我们可能应该给它一个 👍 并描述我们迫切的使用案例。我不确定是否有更权威的 GitHub 议题,但至少现在,这就是语言本身的现象。
在编译器忘记一些我们认为它应该记住的约束的情况下,我们可以做的是“提醒它”。通常我的做法是:如果类型 XXX 应该受到 YYY 的约束,但编译器没有意识到,我会将 XXX 替换为 Extract<XXX, YYY>,使用 Extract 工具类型通过 YYY 来“过滤” XXX。如果 XXX 真正可以赋值给 YYY,那么这个过滤操作将不会产生效果,但现在编译器会识别 Extract<XXX, YYY> 可以赋值给 YYY。
所以这给了你这个:
type Keys<T extends RecursiveRecord, K = keyof T> =
K extends string ? (
T[K] extends string ? K :
T[K] extends RecursiveRecord ? K | Keys<Extract<T[K], RecursiveRecord>> :
never
) : never;
这解决了错误。
当然,对于这个操作,我可能会写得更像这样:
type NestedKeys<T> =
T extends object ? { [K in keyof T]-?: K | NestedKeys<T[K]> }[keyof T] : never;
至少对于你的例子,这产生了相同的结果:
type B = NestedKeys<Obj>
// type B = "a" | "b" | "c" | "d" | "e" | "f"
代码的 Playground 链接
