简体   繁体   English

TypeScript:获取嵌套 object 键的联合类型

[英]TypeScript: Get union type of nested object keys

I wanted to extract types of keys of nested objects and tried something like the below.我想提取嵌套对象的键类型并尝试了类似下面的方法。

TS Playground link TS Playground 链接

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"

But on the K | Keys<T[K]>但在K | Keys<T[K]> K | Keys<T[K]> , I got the following type error. K | Keys<T[K]> ,我收到以下类型错误。 Is there any clean way to solve this?有什么干净的方法可以解决这个问题吗?

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'.

There's at least one open issue in TypeScript's GitHub repo about this problem: see microsoft/TypeScript#25804 for a suggestion to allow conditional types to keep track of constraints on more complicated checked types like T[K] . TypeScript 的 GitHub 存储库中至少有一个关于此问题的未解决问题:请参阅microsoft/TypeScript#25804以获取允许条件类型跟踪对更复杂的检查类型(如T[K]的约束的建议。 Right now that one is just listed as "awaiting more feedback" so if we want to see anything done about it we should probably give it an and describe our compelling use cases.现在,它只是被列为“等待更多反馈”,所以如果我们想看到关于它的任何事情,我们可能应该给它一个并描述我们引人注目的用例。 I'm not sure if there's a more canonical GitHub issue for it, but for now, anyway, it's just the way the language is.我不确定是否有更规范的 GitHub 问题,但现在,无论如何,这只是语言的方式。

What we can do in situations where the compiler forgets some constraint we think it should remember is to "remind it".在编译器忘记一些我们认为它应该记住的约束的情况下,我们可以做的是“提醒它”。 Usually my approach is: if the type XXX is supposed to be constrained by YYY but the compiler doesn't realize it, I replace XXX with Extract<XXX, YYY> , using the Extract utility type to "filter" XXX by YYY .通常我的方法是:如果类型XXX应该受YYY约束但编译器没有意识到,我将XXX替换为Extract<XXX, YYY> ,使用Extract实用程序类型通过YYY “过滤” XXX If XXX is truly assignable to YYY then this filter will be a no-op, but now the compiler will recognize that Extract<XXX, YYY> is assignable to YYY .如果XXX真的可以分配给YYY ,那么这个过滤器将是一个空操作,但现在编译器会识别出Extract<XXX, YYY>可以分配给YYY

So that gives you this:所以这给了你这个:

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;

which resolves the error.这解决了错误。


Of course for this operation I'd probably write something more like:当然,对于这个操作,我可能会写一些更像:

type NestedKeys<T> =
  T extends object ? { [K in keyof T]-?: K | NestedKeys<T[K]> }[keyof T] : never;

which, at least for your example, yields the same result:至少对于您的示例,这会产生相同的结果:

type B = NestedKeys<Obj>
// type B = "a" | "b" | "c" | "d" | "e" | "f"

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM