如何在TypeScript中定义绑定联合类型成员的函数类型?
TypeScript类型定义:绑定values数组与renderer函数类型并支持联合类型
问题场景
现有代码尝试将values数组与renderer函数的类型绑定,但TypeScript报错提示number无法赋值给string,原代码如下:
interface MyObject { select: SelectData<string | number> } interface SelectData<T extends string | number> { values: T[] renderer: (values: T[]) => string } // 报错:renderer的参数类型number[]无法匹配(string | number)[] const obj: MyObject = { select: { values: [1, 2], renderer: (values: number[]) => 'sth' } } const obj2: MyObject2 = { select: { values: ['one', 'two'], renderer: (values: string[]) => 'sth' } }
需求:
- 当
values为某一类型时,renderer仅接受该类型的数组作为参数; - 支持多种可接受的类型,理想情况下通过联合类型定义。
解决方案
核心问题在于原定义中SelectData<string | number>会让values和renderer的参数都变成(string | number)[],但你需要的是**values和renderer参数类型严格一致的独立类型的联合**,而非一个同时兼容两种类型的单一类型。
方案一:直接使用联合类型
将MyObject的select属性定义为SelectData<string> | SelectData<number>,这样TypeScript会自动识别每个子类型的结构,确保values和renderer的类型绑定:
interface SelectData<T extends string | number> { values: T[] renderer: (values: T[]) => string } interface MyObject { select: SelectData<string> | SelectData<number> } // 正常通过类型检查 const obj: MyObject = { select: { values: [1, 2], renderer: (values: number[]) => 'sth' } } // 正常通过类型检查 const obj2: MyObject = { select: { values: ['one', 'two'], renderer: (values: string[]) => 'sth' } } // 报错:values是string[]但renderer接受number[],类型不匹配 const invalidObj: MyObject = { select: { values: ['one', 'two'], renderer: (values: number[]) => 'sth' } }
方案二:自动生成联合类型(适用于扩展更多类型的场景)
如果需要支持更多类型(比如boolean),可以用分发式条件类型自动生成所有子类型的SelectData联合,避免手动枚举:
interface SelectData<T extends string | number | boolean> { values: T[] renderer: (values: T[]) => string } // 分发式条件类型:将联合类型T拆分为每个子类型的SelectData联合 type SelectDataUnion<T> = T extends string | number | boolean ? SelectData<T> : never interface MyObject { select: SelectDataUnion<string | number | boolean> } // 正常通过类型检查 const boolObj: MyObject = { select: { values: [true, false], renderer: (values: boolean[]) => 'bool' } }
原理说明
当你使用SelectData<string> | SelectData<number>时,TypeScript会进行联合类型的判别:
- 对于
{ values: number[], renderer: (number[]) => string },它匹配SelectData<number>; - 对于
{ values: string[], renderer: (string[]) => string },它匹配SelectData<string>; - 任何
values和renderer类型不匹配的结构都会被TypeScript拦截,满足你的类型绑定需求。
而原定义中SelectData<string | number>会让renderer的参数要求是(string | number)[],但(number[]) => string不能赋值给((string | number)[]) => string(函数参数是逆变位置,更窄的类型不能赋值给更宽的类型),这就是报错的原因。
内容的提问来源于stack exchange,提问作者Faire
相关产品推荐
相关产品推荐

