TypeScript:扩展数组元素函数的类型校验异常排查
问题:TypeScript数组元素扩展函数的类型错误排查
我尝试编写一个TypeScript函数,为数组中的每个元素执行回调函数并扩展新属性,具体代码如下:
interface Row { a: number } export function extender< Data extends Row[], T extends object, Props extends keyof T >(data: Data, extend: (row: Row) => T, newProp?: Props): (T & Row)[] { return data.map(row => ({ ...row, ...extend(row) })) }
当调用时不使用回调的row参数,代码可以正常运行:
extender([{ a: 1 }, { a: 2 }], () => ({ once: 1, more: 2 }), 'once')
但当在回调中使用row参数时,会出现类型错误:Argument of type '"once"' is not assignable to parameter of type 'undefined',具体调用代码如下:
// TS_ERROR: Argument of type '"once"' is not assignable to parameter of type 'undefined'. extender([{ a: 1 }, { a: 2 }], (row) => ({ once: 1, more: row.a * 2 }), 'once')
请问忽略了什么细节导致该错误?
原因与解决方案
问题出在TypeScript的类型推断逻辑上:
- 当回调不使用
row参数时,TS能直接推断出T是具体的对象类型{ once: number; more: number },因此keyof T是"once" | "more",newProp参数可以正常接收"once"。 - 当回调使用
row参数时,TS会错误地将T推断为依赖于row参数的泛型类型(而非具体对象类型),导致keyof T被解析为never,此时newProp的类型就变成了undefined,自然无法传入"once"。
修正方案
移除多余的Data extends Row[]泛型参数,让T仅代表回调返回的具体对象类型,同时让回调参数与输入数组的元素类型绑定:
interface Row { a: number } export function extender< T extends object, Props extends keyof T >(data: Row[], extend: (row: Row) => T, newProp?: Props): (T & Row)[] { return data.map(row => ({ ...row, ...extend(row) })) }
如果需要支持Row的子类型数组(保留输入数组的具体类型),可以调整为:
interface Row { a: number } export function extender< R extends Row, T extends object, Props extends keyof T >(data: R[], extend: (row: R) => T, newProp?: Props): (R & T)[] { return data.map(row => ({ ...row, ...extend(row) })) }
修正后再调用包含row参数的回调,类型检查就能正常通过了。
内容的提问来源于stack exchange,提问作者yurk
相关产品推荐
相关产品推荐

