打字稿泛型:从函数参数的类型推断类型?

Posted

技术标签:

【中文标题】打字稿泛型:从函数参数的类型推断类型?【英文标题】:Typescript generics: infer type from the type of function arguments? 【发布时间】:2020-03-22 02:54:53 【问题描述】:

我有一个有 2 个参数的方法,我希望它从第一个参数推断出一个类型。

例如,在下面的代码中,我希望从firstArgument 中推断出函数create_C<T> 的类型T,以便create_C 函数的返回类型为C<type inferred from firstArgument>

interface C<T>  
    firstArgument: A<T>;
    secondArgument: (obj: any) => T


export interface A<T> 
    type: T;


function create_C<T>(
    firstArgument: A<T>,
    secondArgument: (obj: any) => T
): C<T> 
    return 
        firstArgument,
        secondArgument
    

但是,在下面的实现中,const c 的类型被推断为C&lt; prop2: number &gt;。但我希望它被推断为C&lt;B&gt;,并且我希望编译器会抛出一个错误,指出secondArgument 的返回类型不是B 类型

interface B  
    prop1: string;
    prop2: number


export class B_Component implements A<B> 
    type: B = 
        prop1: "",
        prop2: 1
    ;


const c = create_C(
    new B_Component(),
    () => ( prop2: 2 )
)

如何确保编译器抛出错误,指出 secondArgument 的返回类型不是 B 类型?

这里是 Stackblitz 编辑器链接:https://stackblitz.com/edit/qqddsn

【问题讨论】:

不完全关注但你不应该像这样打电话给create_C吗?:create_C&lt;B_Component&gt;()。您使用泛型键入函数,一切看起来都不错,只需在调用时将正确的类型传递给 T... 那么您是否希望您的create_C 调用出错?作为第二个参数传递的回调不返回B,它返回prop2: number,所以cC&lt;prop2: number&gt;。为什么不在回调中返回一个实际的B @MrRobboto 我不想用泛型调用函数。我希望根据传递的第一个参数的类型推断泛型 很确定您不应该将 create_C 键入为采用泛型 then 并且应该读入 @jcalz 评论 - () =&gt; ( prop2: 2 ) 不适合您使用 secondArgument: (obj: any) =&gt; T 键入的内容 【参考方案1】:

在你的函数签名中

declare function create_C<T>(a1: A<T>, a2: (obj: any) => T): C<T>;

T 有两个推理站点(“推理站点”的意思是“编译器可以用来尝试为类型参数推断类型的某个地方”)。一个站点来自第一个参数a1type 属性,另一个站点是第二个参数a2 的返回类型。编译器会查看类似的调用

create_C(new B_Component(), () => ( prop2: 2 );

并尝试从两个站点推断T。在这种情况下,有一个匹配项:(new B_Component()).typeprop2: 2 都可以分配给 prop2: number。所以没有错误,你会得到C&lt;prop2: number&gt; 出来。在另一种情况下,这可能正是您希望编译器提供的行为。


相反,您希望看到编译器只使用a1 来推断T,并验证a2 是否匹配它。也就是说,您希望(obj: any) =&gt; T 中的T 成为non-inferential type parameter (see microsoft/TypeScript#14829)。不幸的是,对此没有“官方”支持。但幸运的是,通常可以使用一些变通方法来获得这种行为。

这是一种这样的技术:如果您将推理站点中的类型参数从T 更改为T &amp; ,则它是lowers the site's priority。因此,编译器将倾向于首先从其他推理站点推断T,并且只有在无法从其他位置推断时才返回T &amp; 。并且T &amp; 类型与T 非常相似(如果T 是一个对象类型,那么它基本上是相同的)所以它不会对语义有太大的改变。让我们试试吧:

declare function create_C_better<T>(a: A<T>, b: (obj: any) => T & ): C<T>;

这里是:

const c2 = create_C_better(
    new B_Component(),
    () => ( prop2: 2 ) // error!
    //    ~~~~~~~~~~~~~~ <-- prop1 is missing
)

const c3 = create_C_better(
    new B_Component(),
    () => ( prop1: "all right", prop2: 2 )
); // C<B>

在那里,当prop1 丢失时,你会得到你想要的错误,当你修复它时,你会根据需要得到C&lt;B&gt; 类型的输出。


好的,希望对您有所帮助;祝你好运!

Link to code

【讨论】:

很好的解释。应该在代码中引用,因为没有人会知道&amp; 的用途 感谢您抽出宝贵时间提供帮助!真的很棒的解释。【参考方案2】:

这是由于secondArgument: (obj: any) =&gt; T。 如果您将上述定义应用于() =&gt; ( prop2: 2 ) T 的类型是 prop2: number 。您可以将其更改为其他内容以获得所需的结果。 例如。

    interface C<T> 
      firstArgument: A<T>;
      secondArgument: (obj: any) => any;
    

    export interface A<T> 
      type: T;
    

    declare function create_C<T>(
      firstArgument: A<T>,
      secondArgument: (obj: any) => any
    ): C<T>;

    interface B 
      prop1: string;
      prop2: number;
    

    export class B_Component implements A<B> 
      type: B;
      configuration: B = 
        prop1: "",
        prop2: 1
      ;
    
    const b = new B_Component();
    export const c = create_C(b, () => ( prop2: 2 ));

【讨论】:

嘿,你发布的那个东西还是不行。 T 的类型是 STILL prop2: number 这很奇怪。请检查这个 ts 游乐场链接。 typescriptlang.org/play/… 鼠标悬停在 c 上是预期的类型。 但它并不强制第二个参数的返回类型为 B 我明白你的意思。就像第一个答案 ` (obj: any) => T & ` 应该修复它。

以上是关于打字稿泛型:从函数参数的类型推断类型?的主要内容,如果未能解决你的问题,请参考以下文章

从函数回调推断泛型类型参数

TypeScript 中对象字面量的动态泛型类型推断

来自接口实现的 Typescript 泛型推断

为啥 C# 无法从非泛型静态方法的签名推断泛型类型参数类型?

打字稿泛型返回类型

打字稿重载、可选参数和类型推断