如何使函数基于具有子属性的数组来推断类型?

时间:2019-07-02 21:48:46

标签: typescript

我在创建函数签名时遇到问题,该函数签名可以基于具有该类型属性的对象数组来推断联合类型。如果仅是对象/类型的数组,它就可以工作,但是我觉得它也应该以这种方式工作。如果不清楚,在下面的示例中,我希望推断的函数签名为func<ClassA | ClassB>(arr: Array<Config<ClassA | ClassB>>)

interface Base {
    thing: number;
}
class ClassA implements Base {
    public propA!: number;
    public thing!: number;
}
class ClassB implements Base {
    public propB!: number;
    public thing!: number;
}
interface Config<T extends Base> {
    cls: new() => T;
}

function func<T extends Base>(arr: Array<Config<T>>) {}

func([]);

func([
  { cls: ClassA },
]);

// Doesn't compile
func([
  { cls: ClassA },
  { cls: ClassB },
]);

// Does compile
func<ClassA | ClassB>([
  { cls: ClassA },
  { cls: ClassB },
]);

这里有一个关于stackblitz的例子 https://stackblitz.com/edit/typescript-array-subprop-type-infer

如果您查看第42行,则可以看到它不是子属性时正确地将类型推断为ClassA | ClassB。但是在第24行,它没有将其推断为该类型。它仅获取数组中第一个元素的类型,因此将鼠标悬停在其上时的函数签名为ClassA(或在stackblitz上显示基类)。我希望它能与任何数量的类型一起使用,因此将它们添加到功能模板参数中是不够的。

1 个答案:

答案 0 :(得分:1)

问题是您正试图强制TypeScript推断

Array<Config<ClassA | ClassB>>

在尝试推断时

Array<Config<ClassA> | Config<ClassB>>

通常,这些关系是不可互换的。尽管作为声明可能会感到尴尬,但您可以做的是允许推断正确类型的方法是

function func<T extends Config<Base>>(arr: Array<T>) {}

但随后必须进行显式调用

func<Config<ClassA> | Config<ClassB>>([
  { cls: ClassA },
  { cls: ClassB },
]);