我试图理解如何在Typescript中进行递归类型输入,并获得编译错误。
我有以下两个功能-
getNestedProperty
-递归函数返回对象的嵌套属性的值sum
-接受对象数组并对嵌套属性的值求和的函数
function getNestedProperty<T extends Record<string, T>, P extends keyof T>(obj: T, keys: string[]): T | T[P] {
const nestedKeys = [...keys];
const nestedKey = nestedKeys.shift();
if (nestedKey !== undefined) {
return getNestedProperty(obj[nestedKey], nestedKeys) as T;
} else {
return obj;
}
}
export function sum<T extends number | Record<string, T>>(array: T[], keys?: string[]): number {
const totalSum = array.reduce((accumulator: number, element: T) => {
if (typeof element === 'number') {
return accumulator + element;
}
if (keys === undefined) {
throw new Error(`keys not passed in as an argument and type of Array is not number`);
}
return accumulator + +getNestedProperty(element, keys);
}, 0);
return totalSum;
}
我在尝试调用sum
函数时得到此错误
const array = [
{ a: { b: 1 } },
{ a: { b: 3 } },
{ a: { b: 5 } }
];
const actual = Utils.sum(array, ['a', 'b']);
expect(actual).to.equal(1+3+5);
Error:
Argument of type '{ a: { b: number; }; }[]' is not assignable to parameter of type '(number | Record<string, { a: { b: number; }; }>)[]'.
Type '{ a: { b: number; }; }' is not assignable to type 'number | Record<string, { a: { b: number; }; }>'.
Type '{ a: { b: number; }; }' is not assignable to type 'Record<string, { a: { b: number; }; }>'.
Property 'a' is incompatible with index signature.
Property 'a' is missing in type '{ b: number; }' but required in type '{ a: { b: number; }; }'.ts(2345)
1条答案
按热度按时间e4eetjau1#
generic函数的调用签名类似于
当你用一些对象字面量调用它时
编译器没有使用
number | Record<string, T>
作为T
的 * 推断点 *(如建议的那样,但从未在microsoft/TypeScript#7234中采用)。相反,它仅从传入的值推断T
,然后根据约束 * 检查 * 它。因此在上面的调用中,
T
被推断为{a: 1}
,因为这是{a: 1}
文字的类型。然后根据number | Record<string, T>
进行检查,number | Record<string, T>
是number | Record<string, {a: 1}>
。并且{a: 1}
不 * 扩展number | Record<string, {a: 1}>
,因此值不满足约束,并且存在错误。调用成功的唯一方法是如果传入的值已经知道是以所需的方式递归的,但如果传入的是对象文字,则不会发生这种情况。
所以我们需要一个不同的方法。
除非您需要泛型,否则您可以将函数设置为非泛型,并在必要时使它们作用于适当的递归类型,如
因此,之前的值将针对该类型进行检查,结果成功:
所以你可以重构为:
也许
这里我们只说
getNestedProperty(obj, keys)
的输入和输出类型是unknown
,因为如果不使函数通用化,就没有什么更有用的了。请注意,我可能会将实现重构为
除非你需要递归。
如果您确实需要使函数具有通用性,那么您必须经历一个相对痛苦的过程,即准确描述函数将执行的操作。
尽管我不想进一步离题来解释它是如何工作的。
Playground链接到代码