【问题标题】:Nested generics in typescript打字稿中的嵌套泛型
【发布时间】:2021-09-10 23:57:15
【问题描述】:

我有以下代码:

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&lt;typeof obj2&gt;) 中的类型的情况下从嵌套函数中使用泛型?谢谢。 你可以在这里看到:https://tsplay.dev/mbk7BW

【问题讨论】:

  • 是的,但是如果我添加一个泛型参数,代码仍然不能按我想要的方式工作
  • 恐怕不清楚你在问什么。你似乎在问你展示的代码是如何工作的,但我认为你一定是在问别的问题。
  • 您是否考虑过反转柯里化(以便您先传递对象,然后传递您要处理的字段)?
  • @raina77ow 我想要这个语法:)
  • @T.J.Crowder 更新了示例,它可能会变得更清晰。我想在嵌套函数的第一个函数中使用泛型类型

标签: typescript typescript-generics


【解决方案1】:

嗯,类型推断在 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} &amp; {b1: any}...,因此 result 的类型为 {a1: any} &amp; {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} &amp; {b: any} 是编译器可以做到的最好的类型做)。相反,我们在 K 中将其设为通用,ab 的键类型的联合。

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

现在当你调用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

【讨论】:

    猜你喜欢
    • 2021-12-20
    • 1970-01-01
    • 2020-12-17
    • 2021-12-06
    • 2019-07-30
    • 2016-10-19
    • 2015-11-27
    • 1970-01-01
    • 2021-11-26
    相关资源
    最近更新 更多