如何在类工厂中多态使用类类型参数且避免any类型?
用unknown替代any实现TypeScript类工厂的正确方案
直接使用unknown[]作为构造函数的参数类型会导致外部调用时构造签名不兼容,核心原因是TypeScript中构造签名的参数属于逆变位置,宽泛的unknown[]无法匹配具体的参数类型数组。解决这个问题的关键是用泛型捕获具体的构造参数类型,而非直接使用unknown[]硬编码。
正确实现代码
首先定义泛型构造类型,用unknown替代any作为默认类型约束:
type Constructor<T = unknown, Args extends unknown[] = unknown[]> = new (...args: Args) => T;
然后实现类工厂函数,通过泛型推导捕获目标类的构造参数和实例类型:
function createInstance<T, Args extends unknown[]>(ctor: Constructor<T, Args>, ...args: Args): T { return new ctor(...args); }
使用示例
比如定义一个带具体构造参数的类:
class User { constructor(public username: string, public email: string) {} } // 内部调用 const user = createInstance(User, "john_doe", "john@example.com"); console.log(user.username); // 类型推导为string,无错误 // 外部依赖调用(比如另一个模块导入此工厂函数) // 同样可以正确推导参数类型,不会出现构造签名不兼容问题
为什么之前的方案会出错
当你直接定义type SymbolConstructor = new (...args: unknown[]) => symbol时,外部传入的类构造函数(比如new (username: string, email: string) => User)的参数类型是更具体的数组,而逆变位置要求传入的类型必须等于或更窄于声明的类型。unknown[]是最宽泛的数组类型,无法匹配具体的参数数组,因此会触发类型错误。
而泛型版本通过Args推导目标类的具体参数类型,让构造签名的参数类型完全匹配,从根本上解决了不兼容问题,同时全程使用unknown替代any,保持类型安全。
内容的提问来源于stack exchange,提问作者Mike Lischke
相关产品推荐
相关产品推荐

