简体   繁体   English

Typescript generics,约束和文字类型

[英]Typescript generics, constraints and literal types

I have the following generic function:我有以下通用 function:

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

If I attempt to use this function like so, I get errors that I cannot reassign.如果我尝试像这样使用这个 function,我会收到无法重新分配的错误。

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'

I would expect the type returned from identity to be either a string or number , however it seems I am getting literal types back of 'a' and 1 .我希望从identity返回的类型是stringnumber ,但似乎我得到的是'a'1的文字类型。 Why is this the case?为什么会这样? and is there a way to change my constraints so this is the behaviour?有没有办法改变我的约束,所以这是行为?

I've have found a couple of workarounds.我找到了几个解决方法。 Firstly to assign the parameter to a variable first:首先将参数赋值给一个变量:

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

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

And secondly to cast to a string or number :其次转换为stringnumber

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

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

Both of these feel wrong.这两种感觉都不对。 I'm still learning TS so I think I've missed something.我还在学习 TS,所以我想我错过了一些东西。

The type parameter will normally be inferred as the most specific possible type consistent with the argument;类型参数通常会被推断为与参数一致的最具体的可能类型; in this case, the string literal type 'a' .在这种情况下,字符串文字类型为'a' This is almost always the desired behaviour.这几乎总是期望的行为。

In your case, the inferred type is too strict because you are using it as the inferred type of a variable, not just the inferred type of an expression.在您的情况下,推断类型过于严格,因为您将其用作变量的推断类型,而不仅仅是表达式的推断类型。 So a natural solution is to specify a weaker type parameter, like so:所以一个自然的解决方案是指定一个较弱的类型参数,如下所示:

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

However, it would be more normal to simply specify the type of the variable:但是,简单地指定变量的类型会更正常:

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

You're using a generic but your requirements are antithetical to generics. This is because identity('a') actually means identity<'a'>('a') therefore the return type is 'a' (not any string).您使用的是泛型,但您的要求与 generics 是对立的。这是因为identity('a')实际上意味着identity<'a'>('a')因此返回类型是'a' (不是任何字符串)。

What works (for some reason) is:有效的(出于某种原因)是:

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

I think how this works is it resolves which of the overloads best matches within the type Identity when you call the function which implements that type.认为这是如何工作的,当您调用实现该类型的 function 时,它可以解决类型Identity中哪些重载最匹配。

Playground link 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM