具有递归,条件和匹配键的TypeScript泛型

时间:2019-01-17 16:48:24

标签: typescript generics typescript-generics

我具有以下带有2个通用参数的类型:

type Result<INPUT, SPEC> = ...

Result的具体类型取决于INPUTSPEC的各种组合。

应如何工作:

if(INPUT extends {[key: string]: any}) {
  if(SPEC extends {[key: string]: any}) {
    Result = {[key in keyof INPUT]: SPEC[key] !== undefined
            ? Result<INPUT[key], SPEC[key]>
            : true
    }
  } else {
    // ...
  }
} else {
  // ...
}

换句话说:

  • 如果INPUTSPEC是对象,则Result应该是键为INPUT的对象。
  • 对于INPUT的每个键:检查SPEC是否包含相同的键
  • 如果为true,则此密钥的值为Result<INPUT[key], SPEC[key]>
  • 如果为false,则此密钥的值为true

其他信息:

  • 如果INPUTSPEC是对象,则SPECINPUT的子集。这意味着:INPUT可以是{ foo: 42, bar: 42 },但是SPEC只能包含foobar或两者都包含,或者为空。

使用TypeScript可以做到这一点吗?

1 个答案:

答案 0 :(得分:2)

根据您的描述,此类型应该可以解决问题:

export type Result<INPUT, SPEC> = INPUT extends object ? SPEC extends object ? {
    [K in keyof INPUT]: SPEC extends Record<K, infer SPECK> ?
        Result<INPUT[K], SPECK> :
        true
} : never : never;

// r is { foo: true; bar: never; goo: { a: never; b: true; }; }
type r = Result<{
    foo: number,
    bar: string,
    goo: { a: number; b: string }
}, { bar: number,  goo: { a: number }}>

您可能缺少的部分是SPEC extends Record<K, infer SPECK>,它测试类型是否使用键K扩展记录并将该键的类型放入SPECK

您不清楚其他分支上会发生什么(如果其中一个类型参数都不是对象,因此我将never放在此处,但是您可以根据需要进行自定义)

如果我没有100%正确地告诉我,我们可以根据需要进行调整。