TypeScript泛型接口数组赋值报错,如何存储任意Living类型的Speaker?
TypeScript泛型Speaker数组类型不兼容问题解决
错误原因
你遇到的报错是TypeScript函数参数逆变特性导致的类型安全拦截:
Speaker<Living>类型要求其speak方法可以接收任意Living类型(包括Person和Dog)作为参数- 你定义的
michel是Speaker<Person>类型,它的speak方法只能接收Person类型参数,如果强行塞到Speaker<Living>数组中,后续给speak传入Dog参数时就会出现运行时错误,因此TS提前抛出了类型报错。
解决方案
根据不同的使用场景可以选择以下三种实现方式:
场景1:数组需要混装所有Living子类型的Speaker,调用时可自行保证参数类型匹配
用分发条件类型生成允许所有子类型Speaker的联合类型,无需修改原有Speaker定义:
interface Dog { legs: number; } interface Person { arms: number; } type Living = Person | Dog; interface Speaker<T extends Living> { speak: (living: T) => void; }; // 新增:生成所有Living子类型对应的Speaker联合 type AnySpeaker<T extends Living = Living> = T extends unknown ? Speaker<T> : never; const michel: Speaker<Person> = { speak: (person) => console.log(`I have ${person.arms} arms`) }; // 数组类型改为AnySpeaker即可混装所有子类型Speaker const speakers: Array<AnySpeaker> = [michel];
场景2:Speaker设计上支持接收任意Living类型参数
将泛型定义从Speaker接口移到speak方法上:
interface Dog { legs: number; } interface Person { arms: number; } type Living = Person | Dog; // 泛型改在speak方法上 interface Speaker { speak: <T extends Living>(living: T) => void; }; const michel: Speaker = { speak: (living) => console.log(`I have ${(living as Person).arms} arms`) }; const speakers: Array<Speaker> = [michel];
场景3:需要统一传入任意Living参数调用数组中的Speaker,保证运行时安全
给Living类型加标识字段,调用时先做类型匹配再执行方法:
interface Dog { type: 'dog', legs: number; } interface Person { type: 'person', arms: number; } type Living = Person | Dog; interface Speaker<T extends Living> { matchType: T['type'], speak: (living: T) => void; }; const michel: Speaker<Person> = { matchType: 'person', speak: (person) => console.log(`I have ${person.arms} arms`) }; const speakers: Array<Speaker<Living>> = [michel]; // 统一调用方法,自动匹配对应Speaker function invokeSpeaker(speaker: Speaker<Living>, living: Living) { if (speaker.matchType === living.type) { speaker.speak(living as never); } }
内容的提问来源于stack exchange,提问作者Noé
相关产品推荐
相关产品推荐

