我有一个接口,该接口具有取决于T的功能,该功能可以扩展为字符串或数字。
interface IConstructorReg<T extends string | number> {
constructor: new (...args: any[]) => BaseImportObj;
check: (data: T, fileName: string) => boolean;
}
我想推送实现该接口的对象。
function checkFun1(data: string, fileName: string) {
// Severals Checkers ...
return true;
}
const objImp1: IConstructorReg<string> = { constructor: ASCReader, check: checkFun1 };
function checkFun2(data: number, fileName: string) {
// Severals Checkers ...
return true;
}
const objImp2: IConstructorReg<number> = { constructor: ASCReader, check: checkFun2 };
const b: Array<IConstructorReg<string | number>> = [];
b.push(objImp1); // Type 'string | number' is not assignable to type 'string'.
b.push(objImp2); // Type 'string | number' is not assignable to type 'number'.
。数组“ b”的正确类型是什么?
答案 0 :(得分:1)
从根本上讲,这是一个方差问题,即IConstructorReg
的方差。由于T
在函数签名中显示为参数,因此IConstructorReg
在T
中是相反的。这意味着IConstructorReg<string | number>
不是IConstructorReg<string>
的基本类型,而是相反的(相反的矛盾来自继承箭头,指向T
的方向相反,您可以在this答案中了解有关差异的更多信息)
要使其正常工作,您可以执行以下两个操作之一。
您可以将Array<IConstructorReg<string | number>>
更改为Array<IConstructorReg<string> | IConstructorReg<number>>
,这意味着该数组可以包含IConstructorReg<number>
或IConstructorReg<string>
。但是,当您使用数组中的项目时,您需要检查两者中的哪一个才能调用check
,目前您的界面尚无法在运行时执行此检查:
const b: Array<IConstructorReg<string> | IConstructorReg<number>> = [];
b.push(objImp1);
b.push(objImp2);
b[0].check(0, "") // Argument of type 'number' is not assignable to parameter of type 'never'.
(b[0] as IConstructorReg<number>).check(0, "") // type assertion is an option
另一种解决方案是使用方法语法而不是函数字段语法来定义check
。这将使IConstructorReg
中的T
是双变量的。尽管这不需要类型声明或检查,但它本质上是不安全的类型,因为string
或number
可以传递给期望一个或另一个的对象:
interface IConstructorReg<T extends string | number> {
constructor: new (...args: any[]) => BaseImportObj;
check(data: T, fileName: string): boolean;
}
const b: Array<IConstructorReg<string | number>> = [];
b.push(objImp1);
b.push(objImp2);
b[0].check(0, ""); // Ok
b[0].check("0", ""); // Also Ok