Typescript 泛型,约束和字面量类型

3

我有以下通用函数:

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 返回的类型应该是一个 string 或者 number,但是实际上我得到的是字面量类型的 '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,所以我想我可能漏掉了点什么。

2个回答

2

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

在您的情况下,推断出的类型过于严格,因为您正在将其用作变量的推断类型,而不仅仅是表达式的推断类型。因此,一个自然的解决方案是指定一个更弱的类型参数,像这样:

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

然而,更常见的做法是直接指定变量的类型:
let a: string = identity('a');

1

您正在使用一个通用类型,但是您的需求与通用类型相反。这是因为identity('a')实际上意味着identity<'a'>('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最匹配的重载版本。

示例链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接