TypeScript实现泛型约束的构造函数实例化函数及相关限制问题
问题描述
我希望实现一个行为如下的函数:
GetComponent<T>(type : typeof T): T { ... }
该函数需接收类型为T的构造函数作为参数,并返回T类型的实例。此外,当传入的构造函数与指定类型不匹配时(如GetComponent<Vec2>(Vec3)),需触发编译错误。请问能否实现该功能?
另外,是否可以禁止传入抽象类型?
我曾尝试如下实现:
class Resources { static Load<T extends AssetObject>(type: typeof AssetObject, id: string): T { return Resources.load_sync_impl(id, type) as T; } }
但在编写List类时,无法将构造函数参数限制为类中定义的类型:
class List<T> { constructor(type: any) { // <<======= 我不得不将其设为any以支持私有构造类 this._type = type; } }
此前的方案类似:
constructor(type: new (...args: any[]) => T);
解决方案
1. 实现类型匹配的GetComponent<T>并触发编译错误
完全可以实现。你需要把参数类型定义为new (...args: any[]) => T(T的构造函数类型),结合泛型约束后,TypeScript会在编译时自动检查传入的构造函数是否和T匹配。示例代码:
function GetComponent<T>(type: new (...args: any[]) => T): T { // 这里写实例化逻辑,比如 return new type(); return new type(); } // 测试用类 class Vec2 { x: number; y: number; } class Vec3 { x: number; y: number; z: number; } // 正确调用,无编译错误 const vec2 = GetComponent<Vec2>(Vec2); // 错误调用,直接触发编译错误:类型typeof Vec3的参数不能赋值给类型new () => Vec2的参数 const vec3 = GetComponent<Vec2>(Vec3);
2. 禁止传入抽象类型
可以实现,需要借助TypeScript 4.7+版本支持的抽象类类型判断,通过泛型约束拦截抽象构造函数。先定义一个工具类型,再修改函数约束:
// 判断是否为抽象构造函数的工具类型 type IsAbstract<T> = T extends abstract new (...args: any[]) => any ? true : false; function GetComponent<T extends new (...args: any[]) => InstanceType<T>>( type: T extends abstract new (...args: any[]) => any ? never : T ): InstanceType<T> { return new type(); } // 测试用类 abstract class AbstractAsset {} class ConcreteAsset extends AbstractAsset {} // 错误调用,传入抽象类会触发编译错误 const abstractInst = GetComponent(AbstractAsset); // 正确调用 const concreteInst = GetComponent(ConcreteAsset);
这里利用条件类型,如果传入的是抽象构造函数,就把参数类型设为never,强制触发编译错误。
3. 解决List类构造函数支持私有构造类的问题
之前用new (...args: any[]) => T无法支持私有构造类,是因为私有构造函数的类,其构造函数类型无法被外部访问。改用typeof T结合泛型约束即可解决:
class List<T> { private _type: typeof T; constructor(type: typeof T) { this._type = type; } } // 私有构造类测试 class PrivateCtorClass { private constructor() {} } // 正确实例化List,无编译错误 const list = new List(PrivateCtorClass);
注意:这种写法只能存储构造函数引用,无法直接通过_type实例化对象(因为构造函数是私有的)。如果需要在List内部创建实例,得依赖目标类的静态方法:
class PrivateCtorClass { private constructor() {} static create() { return new PrivateCtorClass(); } } class List<T> { private _type: typeof T; constructor(type: typeof T) { this._type = type; } createInstance(): T { // 调用静态方法创建实例 return (this._type as { create: () => T }).create(); } }
内容的提问来源于stack exchange,提问作者laoguang
相关产品推荐
相关产品推荐

