打字稿中的嵌套泛型

时间:2021-06-28 17:04:50

标签: typescript typescript-generics

我有以下代码:

function testFunc<T>(
    a: keyof T,
    b: keyof T
) {
    return function(object2: T): T {
        return object2;
    }
}

const obj2 = {
    a1: 1,
    b1: 2,
    c1: 3,
    e1: 4,
    d1: 5
}

const result = testFunc('a1', 'b1')(obj2);
const c1 = result.c1; // Error. Property 'c1' does not exist on type '{ a1: any; } & { b1: any; }'. It's wrong

// We can try this

const result2 = testFunc<typeof obj2>('a1', 'b1')(obj2);
const c12 = result2.c1; // Works great

如何在不显式指定 testFunc (testFunc<typeof obj2>) 中的类型的情况下从嵌套函数中使用泛型?谢谢。 你可以在这里看到:https://tsplay.dev/mbk7BW

1 个答案:

答案 0 :(得分:2)

嗯,类型推断在 TypeScript 中不起作用。只要你调用testFunc('a1', 'b1'),编译器就已经指定了泛型类型参数T;没有办法推迟该规范。换句话说:编译器不能 contextually typetestFunc('a1', 'b1') 返回的函数基于您继续将 obj2 传递给它的事实。就好像你写了下面的代码:

const returnedFunction = testFunc('a1', 'b1');
/* const returnedFunction: (object2: { a1: any;} & { b1: any;}) => 
   { a1: any; } & { b1: any; } */

const result = returnedFunction(obj2);
/* const result: { a1: any; } & { b1: any; } */

const c1 = result.c1; // oops

returnedFunction 的类型是一个函数,其输入和输出的类型为 {a1: any} & {b1: any}...,因此 result 的类型为 {a1: any} & {b1: any},编译器对此一无所知它的 c1 属性。


为了解决这个问题,我建议更改类型参数,以便 returnedFunction 仍然有足够的信息来执行您想要的操作:

function testFunc<K extends PropertyKey>(
    a: K,
    b: K
) {
    return function <T extends Record<K, any>>(object2: T): T {
        return object2;
    }
}

这里,testFuncT 中不是通用的,因为 ab 没有包含足够的信息来为 T 生成一个非常有意义的类型({a: any} & {b: any} 是编译器所能做的最好的)。相反,我们在 K 中使其泛型,ab 的键类型的并集。

然后,返回的函数本身在 T 中是泛型的,对于至少具有 K 中的键的对象类型来说是 constrained,但可能更多。

现在当你调用 testFunc('a1', 'b1') 时,结果是强类型的:

const returnedFunction = testFunc('a1', 'b1');
/* const returnedFunction: 
   <T extends Record<"a1" | "b1", any>>(object2: T) => T */

因此,当您将 obj2 传递给它时,它会执行您想要的操作:

const result = returnedFunction(obj2);
/* const result: {
  a1: number;
  b1: number;
  c1: number;
  e1: number;
  d1: number;
} */

const c1 = result.c1; // okay, number

既然它在您保存中间结果时有效,您可以停止这样做,它仍然有效:

const result = testFunc('a1', 'b1')(obj2);
const c1 = result.c1; // okay, number

Playground link to code