TypeScript中为何无法将类赋值给抽象类类型变量?
问题重现
我有一段可正常运行的TypeScript代码:
type Class<T> = new (...args: unknown[]) => T; abstract class AbstractClass<T = unknown> { constructor(protected element: T) {} method() { return "Abstract"; } } class A extends AbstractClass<unknown[]> { constructor(element: unknown[]) { super(element); } } class B extends AbstractClass { constructor(element: unknown) { super(element); } method() { return "B"; } } function factoryMethod(flag: boolean): Class<AbstractClass> { return flag ? A : B; } console.log(new (factoryMethod(true))().method()); // 输出 "Abstract" console.log(new (factoryMethod(false))().method()); // 输出 "B"
但执行以下代码时却触发类型错误:
const c: Class<AbstractClass> = A;
错误信息如下:
Type 'A' is not assignable to type 'Class
'. Type 'A' provides no match for the signature 'new (...args: unknown[]): AbstractClass'.
令人困惑的是,同样是将A赋值给Class<AbstractClass>类型,在factoryMethod的返回语句中可以正常工作,直接赋值却不行。
问题原因
核心差异在于TypeScript对函数返回位置的类型检查和直接变量赋值的类型检查采用了不同的严格程度:
Class<AbstractClass>的类型要求:
这个类型定义的是「可以接受任意数量unknown类型参数、返回AbstractClass实例的构造函数」。但A的构造函数仅接受单个unknown[]类型参数,不满足「任意数量参数」的要求——严格来说,A的构造函数签名和Class<AbstractClass>的签名不兼容。函数返回时的类型放宽:
在factoryMethod中返回flag ? A : B时,TypeScript会对联合类型的构造函数启用双向协变的兼容性检查(即使开启了strictFunctionTypes),允许将参数更严格的构造函数赋值给参数更宽泛的类型。直接赋值的严格检查:
而直接给变量c赋值时,TypeScript会执行严格的函数参数逆变检查,此时A的构造函数参数要求(必须传一个unknown[])和Class<AbstractClass>的参数要求(可以传任意参数)不匹配,因此抛出错误。
解决方案
方案1:调整Class类型的泛型定义,适配构造函数参数
给Class类型增加一个泛型参数,用来指定构造函数的参数列表,这样可以更精准地匹配子类的构造函数:
// 新增Args泛型参数,指定构造函数的参数类型 type Class<T, Args extends any[] = any[]> = new (...args: Args) => T; // 明确A的构造函数参数是[unknown[]] const c: Class<AbstractClass<unknown[]>, [unknown[]]> = A;
方案2:使用类型断言(适合需要保持原有Class类型定义的场景)
如果不需要修改Class类型的定义,可以通过类型断言告诉TypeScript跳过严格检查:
const c: Class<AbstractClass> = A as Class<AbstractClass>;
方案3:让factoryMethod返回更精准的联合类型
如果不需要统一返回Class<AbstractClass>,可以让函数返回typeof A | typeof B,这样直接赋值也不会有类型问题:
function factoryMethod(flag: boolean): typeof A | typeof B { return flag ? A : B; } const c = factoryMethod(true); // 类型自动推导为typeof A
内容的提问来源于stack exchange,提问作者Martí Serra Molina

