接口数组的类型取决于T

时间:2020-10-07 08:43:14

标签: arrays typescript typescript-typings

我有一个接口,该接口具有取决于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”的正确类型是什么?

1 个答案:

答案 0 :(得分:1)

从根本上讲,这是一个方差问题,即IConstructorReg的方差。由于T在函数签名中显示为参数,因此IConstructorRegT中是相反的。这意味着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 

Playground Link

另一种解决方案是使用方法语法而不是函数字段语法来定义check。这将使IConstructorReg中的T是双变量的。尽管这不需要类型声明或检查,但它本质上是不安全的类型,因为stringnumber可以传递给期望一个或另一个的对象:

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

Playground Link