【问题标题】:Typescript generics, constraints and literal types打字稿泛型、约束和文字类型
【发布时间】:2021-04-20 22:32:00
【问题描述】:

我有以下通用函数:

type Identity = <T extends string | number>(arg: T) => T;
const identity: Identity = (x) => x;

如果我尝试像这样使用此功能,我会收到无法重新分配的错误。

let a = identity('a');
a = 'b'; // Error: Type '"b"' is not assignable to type '"a"'

let b = identity(1);
b = 2; // Error: Type '2' is not assignable to type '1'

我希望从identity 返回的类型是stringnumber,但似乎我得到了'a'1 的文字类型。为什么会这样?有没有办法改变我的约束,所以这就是行为?

我找到了一些解决方法。首先将参数分配给变量:

let a_param = 'a'
let a = identity(a_param);
a = 'b'; // Yay

let b_param = 1;
let b = identity(b_param);
b = 2; // Yay

其次要转换为stringnumber

let a = identity('a' as string);
a = 'b'; // Yay

let b = identity(1 as number);
b = 2; // Yay

这两种感觉都不对。我还在学习 TS,所以我觉得我错过了一些东西。

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    类型参数通常会被推断为与参数一致的最具体的可能类型;在这种情况下,字符串文字类型为'a'。这几乎总是期望的行为。

    在您的情况下,推断类型过于严格,因为您将其用作变量的推断类型,而不仅仅是表达式的推断类型。所以一个自然的解决方案是指定一个较弱的类型参数,如下所示:

    let a = identity<string>('a');
    

    但是,简单地指定变量的类型会更正常:

    let a: string = identity('a');
    

    【讨论】:

      【解决方案2】:

      您使用的是泛型,但您的要求与泛型相反。这是因为 identity('a') 实际上意味着 identity&lt;'a'&gt;('a') 因此返回类型是 'a'(不是任何字符串)。

      有效的(出于某种原因)是:

      type Identity = {
        (arg: number): number;
        (arg: string): string;
      }
      
      const identity: Identity = (x: any) => x;
      
      let a = identity('a');
      a = 'b'; // yes
      a = 3; // no
      
      let b = identity(1);
      b = 'b'; // no
      b = 3; // yes
      
      

      认为它的工作原理是,当您调用实现该类型的函数时,它会解析类型 Identity 中最匹配的重载。

      Playground link

      【讨论】:

        猜你喜欢
        • 2022-11-23
        • 2019-09-23
        • 2020-08-03
        • 1970-01-01
        • 2021-11-26
        • 1970-01-01
        • 1970-01-01
        • 2020-01-23
        • 2017-09-15
        相关资源
        最近更新 更多