泛型函数使用Array push报错:Type 'string'无法赋值给类型'never'
TypeScript代码中已定义明确的数组类型,但在执行数组push操作时触发类型错误,错误信息如下:
Argument of type 'string | number' is not assignable to parameter of type 'never'.
Type 'string' is not assignable to type 'never'.
相关代码:
type Characters = { letters: string[]; numbers: number[]; } const characters :Characters = { letters: ['a', 'b',' c' ], numbers: [1,2,3], } type AddCharacter = <K extends keyof Characters, V extends Characters[K][number]> (characterType:K, character:V) => void; const addCharacter :AddCharacter = (characterType, character) =>{ characters[characterType].push(character) //Type 'string' is not assignable to type 'never' } addCharacter('letters', 'd') //ok addCharacter('numbers', 4) //ok addCharacter('letters', 4) //error as expected because 4 is not a string addCharacter('numbers', 'd') //error as expected because d is not a number addCharacter('words', 'test' ) //error as expected because 'words' is not a key of 'Characters'
调用addCharacter时的类型检查完全符合预期,但函数内部的push操作却触发上述错误,请问如何消除该错误,让代码与TypeScript良好兼容?
TypeScript无法在函数内部推断出characterType和character之间的类型关联。虽然泛型约束了V是Characters[K][number],但函数内部对characters[characterType]的类型推断会退化为string[] | number[],而character的类型是string | number——由于TypeScript无法确定当前操作的是哪种数组,它会认为只有同时满足两种数组元素类型的值才能被接受,而string和number没有交集,因此得到的类型是never,最终触发错误。
有两种可靠的解决方式:
方式一:使用类型断言明确类型
通过类型断言告诉TypeScript,当前characters[characterType]的元素类型就是V:
const addCharacter: AddCharacter = (characterType, character) => { (characters[characterType] as V[]).push(character); };
方式二:直接定义泛型函数(更推荐)
放弃先定义泛型类型再赋值的方式,直接将函数声明为泛型函数,让TypeScript能直接推断出characterType和character的类型关联:
const addCharacter = <K extends keyof Characters>( characterType: K, character: Characters[K][number] ) => { characters[characterType].push(character); };
这种方式无需额外的类型断言,TypeScript可以准确识别出数组元素类型与传入的character类型完全匹配,错误自然消除。
内容的提问来源于stack exchange,提问作者Luvias

