TypeScript中使用Array<keyof T>元素作为T的索引时的类型兼容问题求助
TypeScript中使用Array元素作为T的索引时的类型兼容问题求助
看起来你遇到的是TypeScript类型检查里的典型场景,我来帮你拆解下问题根源和解决办法。
首先得搞清楚错误为什么会出现:你定义的cols是Array<keyof T>,这意味着它可以包含T的任意键——比如你的user类型里有age(number类型),所以el[col]的类型会变成T[keyof T],也就是所有属性值类型的联合类型(这里是string | number)。而concat方法只接受string或数组作为参数,当el[col]是number类型时,自然就触发了类型不兼容的报错。
接下来给你两种可行的修复方案:
方案一:严格约束传入的列必须对应string类型的属性
这种方式能让TypeScript在调用函数时就检查参数合法性,从根源避免类型问题。我们可以先定义一个工具类型,专门提取对象中属性值为string的键:
// 工具类型:提取T中属性值为string的键 type StringKeys<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T]; interface user { name: string; age: number; lastname: string; } const data: user[] = [ { name: 'foo', age: 21, lastname: 'bar' }, { name: 'foo', age: 21, lastname: 'baz' }, { name: 'foo', age: 21, lastname: 'bax' }, ]; function printLastName<T>(data: T[], cols: StringKeys<T>[]) { data.forEach((el) => { let msg = ''; cols.forEach((col) => { // 这里el[col]的类型已经被约束为string,不会报错 msg += el[col]; }); console.log(msg); }); } // 正常调用完全没问题 printLastName(data, ['name', 'lastname']); // 如果尝试传入'age',TypeScript会直接报错:类型'"age"'不能赋值给类型'StringKeys<user>' // printLastName(data, ['name', 'age']);
方案二:类型断言快速修复(适合临时场景)
如果你能确保自己传入的cols对应的属性都是string类型,也可以用类型断言告诉TypeScriptel[col]是string:
interface user { name: string; age: number; lastname: string; } const data: user[] = [ { name: 'foo', age: 21, lastname: 'bar' }, { name: 'foo', age: 21, lastname: 'baz' }, { name: 'foo', age: 21, lastname: 'bax' }, ]; function printLastName<T>(data: T[], cols: Array<keyof T>) { data.forEach((el) => { let msg = ''; cols.forEach((col) => { // 用as string断言el[col]的类型 msg = msg.concat(el[col] as string); }); console.log(msg); }); } printLastName(data, ['name', 'lastname']);
不过这种方式有个缺点:如果不小心传入了非string属性的键(比如'age'),TypeScript不会提前报错,虽然运行时会把number转成字符串拼接,但不符合类型安全的原则,所以只建议临时用用。
总结一下:追求类型安全优先选方案一,临时快速解决可以用方案二。
备注:内容来源于stack exchange,提问作者Cristian Jara
相关产品推荐
相关产品推荐

