通用函数中的属性类型

时间:2019-11-27 19:50:49

标签: typescript generics types

我正在尝试通过提供字段名称来转换对象的某些字段,目前我写了如下内容:

interface Foo {
    a: number[],
    b: string[],
}

type Bar = { [T in keyof Foo] : (arg : Foo[T]) => Foo[T] }

function test<T extends keyof Foo>(field: T) {
    const foo : Foo = {
        a: [],
        b: [],
    };

    const bar: Bar = {
        a: arg => /* some code */ [],
        b: arg => /* some code */ [],
    };

    foo[field] = bar[field](foo[field]);
}

但是我最终在bar[field](foo[field])上看到以下错误消息:

Argument of type 'Foo[T]' is not assignable to parameter of type 'number[] & string[]'.
  Type 'number[] | string[]' is not assignable to type 'number[] & string[]'.
    Type 'number[]' is not assignable to type 'number[] & string[]'.
      Type 'number[]' is not assignable to type 'string[]'.
        Type 'number' is not assignable to type 'string'.
          Type 'Foo[T]' is not assignable to type 'number[]'.
            Type 'number[] | string[]' is not assignable to type 'number[]'.
              Type 'string[]' is not assignable to type 'number[]'.
                Type 'string' is not assignable to type 'number'

但是应该不应该打字稿“知道”具有相同的TFoo[T]Parameters<Bar[T]>的文字?

1 个答案:

答案 0 :(得分:2)

也许编译器应该知道,但事实并非如此。我倾向于将此问题称为“相关类型”或“相关记录”。编译器将foo[field]bar[field]视为联合类型的东西,这确实是正确的。但是它将它们的类型视为独立的,这意味着,据我们所知,foo[field]可能是number[],而bar[field]可能是一个占用string[]的函数。它没有发现foo[field]的类型与bar[field]的类型相关,以至于知道一个可以修复另一个。有一个未解决的问题,microsoft/TypeScript#30581(我提起,fwiw)表明对关联类型有一些支持,但尚不清楚这是否会发生或如何发生。

我们现在所拥有的只是解决方法。该问题中提到的两种解决方法:要么使用冗余代码来迫使编译器解决各种可能性并保证类型安全,要么使用type assertions放弃某种类型安全性但保持简洁。对于您的代码,它看起来像这样:

// redundant code
const f: keyof Foo = field;
switch (f) {
   case "a":
      foo[f] = bar[f](foo[f]);
      break;
   case "b":
      foo[f] = bar[f](foo[f]);
      break;
}

// type assertion
foo[field] = (bar[field] as <T>(arg: T) => T)(foo[field]);

我通常选择类型断言。好的,希望能有所帮助;祝你好运!

Link to code