typescript - 使用实例类型带工厂功能
问题描述
我有一个相当简单的工厂函数,它使用构造函数映射来根据传入的字符串参数确定要创建的正确类型的对象。
我知道示例中描述的将类构造函数传递给工厂的工厂模式,但在我的情况下,我需要传入一个简单的字符串。
class Vehicle {
public wheels: number;
}
class Car extends Vehicle {
public drive: number
}
class Bike extends Vehicle {
public ride: number;
}
const CTORS = {
car: Car,
bike: Bike
}
type VehicleTypes = typeof CTORS;
function factory<T extends keyof VehicleTypes>(name: T): InstanceType<VehicleTypes[T]> {
let ctor: VehicleTypes[T] = CTORS[name];
// un-comment to see error
// return new ctor();
return new ctor() as InstanceType<VehicleTypes[T]>;
}
let abc = factory('bike');
abc.ride = 5; // type checks ok
上述工作和类型检查正常,但返回时显式键入对于避免编译器错误是必要的:
(Type 'Car | Bike' is not assignable to type 'InstanceType<{ car: typeof Car; bike: typeof Bike; }[T]>'. Type 'Car' is not assignable to type 'InstanceType<{ car: typeof Car; bike: typeof Bike; }[T])
我怀疑打字稿抱怨返回值不是所有潜在实例的联合。但是我不知道如何应用类型,这样就不需要显式的类型覆盖。我也尝试了以下方法,但也无济于事:
type ReturnTypes = {
[P in keyof VehicleTypes]: InstanceType<VehicleTypes[P]>
}
function factory<T extends keyof VehicleTypes>(name: T): ReturnTypes[T] {
let ctor = CTORS[name];
return new ctor();
}
解决方案
这是目前 TypeScript 的一个限制:未解析的条件类型,即那些依赖于尚未指定的泛型类型参数的类型,对编译器是不透明的;它不能真正看到可以分配给它的某些值。类型InstanceType<T>
定义如下:
type InstanceType<T extends new (...args: any) => any> =
T extends new (...args: any) => infer R ? R : any;
这是一个条件类型,在的实现中factory()
,类型InstanceType<VehicleTypes[T]>
是未解析的,因为T
没有指定。
有几个开放的 GitHub 问题提供了使此类未解决的条件类型更易于处理的建议,但目前都没有实现(截至 TS3.7)。如果您足够关心去那里并给他们一个或以其他方式支持他们,这里有一些链接到他们:
- microsoft/TypeScript#13995:泛型类型值应在函数实现中缩小范围
- microsoft/TypeScript#24085:泛型类型参数应在函数实现中缩小
- microsoft/TypeScript#23132:允许受约束的泛型更早地解析条件类型
- microsoft/TypeScript#27808:允许将泛型类型参数限制为单元类型,以便控制流分析可以缩小泛型类型参数
- microsoft/TypeScript#33912:应该使用控制流分析来生成条件返回类型
现在,我会说要么像你一样使用类型断言,要么找到一种不依赖条件类型的方式来表示你的类型。一种可能的前进方式是注意 TypeScript 中的类构造函数被赋予prototype
与其实例类型相同类型的属性。这有点奇怪,也不是很正确,因为实际的原型不会有任何仅实例属性,但这就是它的方式,而且不太可能改变。
InstanceType
因此,您可以使用这个事实制作自己的:
type MyInstanceType<T extends { prototype: any }> = T['prototype'];
然后你可以写
function factory<T extends keyof VehicleTypes>(name: T): MyInstanceType<VehicleTypes[T]> {
let ctor: VehicleTypes[T] = CTORS[name];
return new ctor();
}
没有错误,你仍然得到你期望的类型检查:
let abc = factory('bike');
abc.ride = 5; // type checks ok
希望有帮助;祝你好运!
推荐阅读
- android - 使用 android 中的改造将 pdf / docx 文件(包括图像)上传到服务器
- ruby-on-rails - Masonry-Rails 排水沟打破布局
- android - 在 Android 中使用 ColorMatrix 使图像只有黑白
- r - 为值向量找到不同的区间,使每个区间中的值数大致相等
- amazon-web-services - AWS 无法访问 RDS 实例
- java - 致命异常:android.os.FileUriExposedException 帮助错误
- apache - 如何使 Docker apache2 虚拟主机工作
- php - 使用 php 从数据库中打印数据,该数据库在其列中有一项
- php - WordPress中非唯一列更新的MySQL重复条目
- android - 用于 PhoneGap 的默认 Android Splashcreen 大小是多少