TypeScript判别式索引函数调用类型不兼容问题求解
解决联合类型判别式索引函数调用的TypeScript类型报错问题
先看你的代码场景:
首先定义带判别式的联合类型:
type Component1 = { id: 'component1' num: number } type Component2 = { id: 'component2' str: string } type Component = Component1 | Component2 type ComponentType = Component['id']
接着创建按判别式索引的处理函数对象:
type ComponentConfig = { [K in ComponentType]: (component: Extract<Component, { id: K }>) => void } const cfg: ComponentConfig = { component1(component) { console.log(component.num) }, component2(component) { console.log(component.str) } }
但当用Component类型的变量调用对应处理函数时,TypeScript会报错:
export let component: Component cfg[component.id](component) // TS2345: 类型“Component”的参数不能赋给类型“never”的参数
原因很明确:通过component.id索引cfg得到的是((component: Component1) => void) | ((component: Component2) => void)类型的函数联合。联合类型的函数参数是逆变的,只有参数同时满足所有函数的参数类型时才能调用,而Component1和Component2无交集,所以参数类型被推断为never,导致报错。
无需switch的解决方案
方案1:用泛型函数绑定索引与参数的类型关联
通过泛型让TypeScript追踪id和component的类型对应关系:
const handleComponent = <T extends Component>(component: T) => { cfg[component.id](component); }; // 直接调用即可 handleComponent(component);
泛型T会自动推导为Component1或Component2的具体类型,TS能确认component.id对应的函数参数正好是T,不会报错。
方案2:使用类型断言快速解决
如果确定逻辑上类型安全,可直接用类型断言绕过TS检查:
cfg[component.id](component as Extract<Component, { id: typeof component.id }>);
这种方式更简洁,但要确保业务逻辑不会出现id与组件类型不匹配的情况。
方案3:封装通用的索引调用函数
若需要更通用的写法,可单独封装函数处理索引和调用:
function callHandler<T extends ComponentType>(id: T, comp: Extract<Component, { id: T }>) { cfg[id](comp); } // 调用时 callHandler(component.id, component);
该写法把id和组件分开传入,同样能让TS推导正确的类型对应关系。
内容的提问来源于stack exchange,提问作者Koppa Péter
相关产品推荐
相关产品推荐

