TypeScript中扩展数组类型后传入标准数组为何不报错?
问题描述
我在TypeScript中尝试为应用的特殊场景扩展Array类,代码如下:
enum FruitsEnum { Kiwi = "Kiwi", Pear= "Pear", DragonFruit = "DragonFruit", } class FruitArray extends Array<FruitsEnum | string> { constructor(fruit: FruitsEnum, ...rest: string[]) { console.log(`constructor: ${fruit}`); super(fruit, ...rest); } } const processFruit = (fruit: FruitArray) => { console.log('This is the fruit', fruit); } const main = () => { processFruit(['notAFruit']); }
我不理解的是,为何TypeScript允许执行processFruit(['notAFruit'])?明明指定了processFruit()的参数类型为FruitArray,但传入标准数组却能被接受,这是为什么?
原因解析
这是TypeScript的结构类型系统导致的:TypeScript判断类型兼容性时,核心看两个类型的结构是否匹配,而非类的名义名称。
FruitArray继承自Array<FruitsEnum | string>,它的结构和普通Array<FruitsEnum | string>完全一致,拥有数组的所有方法与索引签名。- 你传入的
['notAFruit']属于string[]类型,而string[]是Array<FruitsEnum | string>的子类型(因为string是FruitsEnum | string的子类型)。 - 由于结构完全匹配,TypeScript认为
string[]可以赋值给FruitArray类型参数,哪怕它并不是FruitArray类的实例。
如果要强制要求参数必须是FruitArray的实例(而非结构匹配的普通数组),可以给FruitArray添加一个唯一标识属性来区分结构:
enum FruitsEnum { Kiwi = "Kiwi", Pear= "Pear", DragonFruit = "DragonFruit", } // 定义唯一符号作为类的标识 const fruitArrayBrand = Symbol('FruitArrayBrand'); class FruitArray extends Array<FruitsEnum | string> { // 添加私有标识属性,普通数组不会拥有该属性 [fruitArrayBrand]: true; constructor(fruit: FruitsEnum, ...rest: string[]) { console.log(`constructor: ${fruit}`); super(fruit, ...rest); this[fruitArrayBrand] = true; } } const processFruit = (fruit: FruitArray) => { console.log('This is the fruit', fruit); } const main = () => { processFruit(['notAFruit']); // 此时TypeScript会报错,普通数组缺少标识属性 }
这样修改后,普通数组因缺少专属标识属性,无法再赋值给FruitArray类型的参数。
内容的提问来源于stack exchange,提问作者Patrick Kenny
相关产品推荐
相关产品推荐

