我正在尝试为一系列类推断静态工厂方法的正确类型,其中父类是通用的。我希望静态工厂方法的返回类型成为抽象两个子类的父类,但打字稿推断两个子类的 or 类型。
abstract class Parent<T> {
abstract readonly property: T
}
class ChildA implements Parent<string> {
constructor(readonly property: string) {}
}
class ChildB implements Parent<number> {
constructor(readonly property: number) {}
}
class Factory {
public static create(guard: any) /** I want to the return type be only Parent without indicate in a explicit way the generic **/ {
if (typeof guard === 'string') {
return new ChildA(guard)
}
if (typeof guard === 'number') {
return new ChildB(guard)
}
return new UnkwonClass()
}
}
我不知道如何将工厂签名描述为只返回 Parent
来抽象两个子类,因为两者都具有相同的形状,并且没有或类型 ChildA | ChildB
我尝试将签名写为父级,然后打字稿告诉我,父级是通用的,然后我将创建方法的签名更改为 public static create<T>(guard: any): Parent<T>
但我必须从实例传递类型,我希望 ts 推断出我传递给子类的类型。
答案 0 :(得分:0)
我认为您遇到的问题是 TypeScript 编译器目前无法使用 control flow analysis 来缩小泛型类型参数的范围,例如 {{1}在以下代码中:
T
当您检查 class Factory {
public static create<T>(guard: T): Parent<T> {
if (typeof guard === 'string') {
return new ChildA(guard); // error!
// Type 'ChildA' is not assignable to type 'Parent<T>'
}
if (typeof guard === 'number') {
return new ChildB(guard); // error!
// Type 'ChildB' is not assignable to type 'Parent<T>'
}
throw new Error('Unknown class')
}
}
时,编译器可以将 typeof guard === "string"
的明显类型从 guard
缩小到 T
。但这不会导致编译器说T & string
本身现在是T
。类型保护缩小了值的类型,而不是泛型类型参数。并且由于 string
未缩小为 T
,因此类型 string
未缩小为 Parent<T>
,因此 Parent<string>
不被视为可分配给 {{1 }}。
GitHub 中有各种未解决的问题,需要在这里进行一些改进。一个好的开始是 microsoft/TypeScript#33014,它要求编译器通过控制流分析缩小类型参数,至少允许某些属性查找。从 TypeScript 4.2 开始,这个和相关的建议都没有实现......而且不清楚这里什么时候或者是否会发生任何变化。
直到并且除非发生某些变化,我的建议是当您比编译器更了解某个值的类型时,您总是可以做的事情:使用 type assertion。 您知道 ChildA
可以在 Parent<string>
时赋值给 ChildA
,所以只需告诉编译器:
Parent<T>
这解决了错误。 (请注意,如果编译器没有将 typeof guard === "string"
视为与 class Factory {
public static create<T>(guard: T): Parent<T> {
if (typeof guard === 'string') {
return new ChildA(guard) as Parent<typeof guard>; // okay
}
if (typeof guard === 'number') {
return new ChildB(guard) as Parent<typeof guard>; // okay
}
throw new Error('Unknown class')
}
}
充分相关,则在您编写 value as Type
时,您的实际代码可能仍会产生错误。如果是这样,您仍然可以通过Type
或 typeof value
。)
请注意类型断言,因为现在您有责任在这些行中验证类型安全。编译器做不到,如果你的断言有误,编译器也不会注意到。不小心向编译器撒谎可能会导致运行时出现奇怪的行为。因此,在断言之前仔细检查 value as unknown as Type
和 value as any as Type
是否真的属于 new ChildA(guard)
类型。