[英]How do I create a generic factory method that access static members of the class type it's creating
[英]Static Factory Method Return Type of Generic Class
我正在尝试为一系列类推断 Static 工厂方法的正确类型,其中父类 class 是通用的。 我希望 static 工厂方法的返回类型成为父 class 以抽象两个子类,但 typescript 推断出两个子类的 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
ChildA | ChildB
我尝试将签名写为 Parent,然后 typescript 告诉我,Parent 是通用的,然后我将 create 方法的签名更改为public static create<T>(guard: any): Parent<T>
但我有从实例传递类型,我希望 ts 推断我传递给子类的类型。
我认为您遇到的问题是 TypeScript 编译器目前无法使用控制流分析来缩小以下代码中的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>
,因此ChildA
不被视为可分配给Parent<string>
。
GitHub 中有各种未解决的问题,要求在此处进行一些改进。 一个好的开始是microsoft/TypeScript#33014 ,它要求编译器通过控制流分析缩小类型参数,至少允许某些属性查找。 从 TypeScript 4.2 开始,该建议和相关建议均未实施......而且尚不清楚何时甚至是否会发生任何变化。
除非有什么改变,否则我的建议是,当你比编译器更了解某个值的类型时,你总是可以做的事情:使用类型断言。 您知道当typeof guard === "string"
时ChildA
可以分配给Parent<T>
,所以只需告诉编译器:
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')
}
}
这解决了错误。 (请注意,如果编译器没有看到Type
与typeof value
充分相关,当您将value as Type
时,您的实际代码仍可能会产生错误。如果是这样,您仍然可以通过value as unknown as Type
或value as any as Type
。)
请注意类型断言,因为现在您有责任在这些行中验证类型安全性。 编译器无法做到这一点,如果您的断言有误,编译器不会在 position 中引起注意。 不小心对编译器撒谎可能会导致运行时出现奇怪的行为。 因此,在断言之前,请仔细检查new ChildA(guard)
和new ChildB(guard)
是否真的是Parent<T>
类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.