简体   繁体   English

为什么Typescript推断'never'而不是交集类型?

[英]Why does Typescript infer 'never' instead of intersection type?

Given the following example: 给出以下示例:

interface Data {
    name: string;
    value: number;
}

const data :Data = {
    name: 'name',
    value: 1,
}

const updateValue = (key: keyof Data, value: string | number): void => {
    data[key] = value;
};

link to ts-playgound 链接到ts-playgound

Typescript shows the following error: Typescript显示以下错误:

Type 'string | number' is not assignable to type 'string & number'.
  Type 'string' is not assignable to type 'string & number'.
    Type 'string' is not assignable to type 'number'.

Which is clear and understandable. 这是清楚易懂的。 However if I add a union type to the interface like so: 但是,如果我将一个联合类型添加到接口,如下所示:

type MultiType = 'x' | 'y';

interface Data {
    name: string;
    value: number;
    multiType: MultiType | null;
}

const data :Data = {
    name: 'name',
    value: 1,
    multiType: null,
}

const updateValue = (key: keyof Data, value: string | number): void => {
    data[key] = value;
};

link to ts-playgound 链接到ts-playgound

I get the following error: 我收到以下错误:

Type 'string | number' is not assignable to type 'never'.
  Type 'string' is not assignable to type 'never'.

Typescript accepts it if I use the intersection type string & number & MultiType but it also accepts never . 如果我使用交集类型string & number & MultiType但是它也接受never ,那么Typescript接受它。

This seems inconsistent to me. 这似乎与我不一致。 Is this maybe a bug? 这可能是个错误吗?

string & number is equivalent to never , and so is string & number & (MultiType | null) . string & number等于neverstring & number & (MultiType | null) There are no values which are both a string and a number , so no values satisfy string & number or string & number & AnythingElse . 没有值既是string又是number ,因此没有值满足string & numberstring & number & AnythingElse

Right now the latter explicitly reduces to never because it contains unions of these equivalent-to- never types, which is really ugly to leave like that. 现在,后者明确地减少到never因为它包含这些等价到never类型的联合,这样做真的很难看。 Specifically, the compiler distributes intersections over unions, so 具体来说,编译器将交叉点分布在联合上,因此

string & number & ('x' | 'y' | null)

becomes

(string & number & 'x') | (string & number & 'y') | (string & number & null)

That type isn't particularly enlightening to people, so the compiler checks that each of those union constituents is equivalent to never and reduces the type to 这种类型对人们来说并不是特别有启发性,因此编译器会检查每个联合组成部分是否等同于never ,并将类型减少为

never | never | never

which is just 这只是

never

as you saw. 如你所见。


So why doesn't string & number by itself get immediately reduced to never ? 那么为什么string & number本身不会立即减少到never Well originally the idea was that it would help people understand where the bug in their code came from, since string is not assignable to number is more enlightening that string is not assignable to never . 最初的想法是,它可以帮助人们了解代码中的错误来自哪里,因为string is not assignable to number更具启发性, string is not assignable to never

Unfortunately while string & number is equivalent to never in terms of what values are assignable to and from it, in TS3.5 and below the compiler doesn't always treat them the same, which is confusing. 不幸的是,虽然string & number 相当于 never分配给它的值,但在TS3.5及更低版本中,编译器并不总是将它们视为相同,这是令人困惑的。

Therefore it looks like, starting in TS3.6, empty intersections like string & number will be reduced to never explicitly . 因此看起来,从TS3.6开始, string & number这样的空交叉将never明确地减少到 Once TS3.6 is released, your above code will act the same in both cases, and you'll get the string | number is not assignable to never 一旦TS3.6发布,你的上述代码在两种情况下的行为都相同,你将获得string | number is not assignable to never string | number is not assignable to never error. string | number is not assignable to never错误。


Okay, hope that helps; 好的,希望有所帮助; good luck! 祝好运!

Two things : 两件事情 :

  • It's only logical that TypeScript doesn't accept string | number TypeScript不接受string | number合乎逻辑的 string | number as type for your value parameter, because all of the following : string | number作为value参数的类型,因为以下所有内容:

    • string | number string | number is not assignable to string because number is not assignable to string in case the key is 'name' string | number不能赋值给string因为如果key'name'number不能赋值给string

    • string | number string | number is not assignable to number because string is not assignable to number in case the key is 'value' (both of there were already true in your first example) string | number不能分配给number因为如果key'value' ,则string不能分配给number (在第一个示例中,两者都已经为真)

    • string | number string | number is not assignable to MultiType | null string | number不能分配给MultiType | null MultiType | null because both number and string are not assignable to 'x' | 'y' | null MultiType | null因为numberstring都不能赋值给'x' | 'y' | null 'x' | 'y' | null 'x' | 'y' | null in case the key is 'multiType' 如果key'multiType''multiType' 'x' | 'y' | null

For some reason however, in your first example TypeScript simply stops on the first "wrong" case and gives you this error (Even though there are really two things wrong already). 但是出于某种原因,在你的第一个例子中,TypeScript只是在第一个“错误”的情况下停止并给你这个错误(即使已经有两个错误了)。

In the second case, you could have seen the same error message because the incompatibility of types is still there, it seems like the inference goes a little deeper and tells you that the problem is deeper than this. 在第二种情况下,您可能已经看到了相同的错误消息,因为类型的不兼容性仍然存在,似乎推理更深入并且告诉您问题比这更深。 As to why is the error message formatted like this, I don't really know and I guess it would require going deeper into how are things inferred by the compiler here. 至于为什么错误消息的格式是这样的,我真的不知道,我想这将需要更深入地了解编译器如何在这里推断出事物。 In the end, the compiler is right, but the error message could be clearer. 最后,编译器是正确的,但错误消息可能更清楚。


For your second question, the never type documentation says : 对于您的第二个问题, never类型的文档说:

The never type is a subtype of, and assignable to, every type never类型是每种类型的子类型,并且可分配给每种类型

So that's why you can specify your value to be of type never , and assign it to your data . 因此,您可以将value指定为never类型,并将其分配给您的data Because it will never happen. 因为它永远不会发生。

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

相关问题 为什么 TypeScript 在使用 concat 减少数组时会推断出“从不”类型? - Why does TypeScript infer the 'never' type when reducing an Array with concat? 为什么 typescript 用递归类型推断未知? - Why does typescript infer unknown with a recursive type? 为什么 TypeScript 有时只将不可能的交叉点视为“从不”? - Why does TypeScript only sometimes treat an impossible intersection as 'never'? TypeScript如何推断“ this”的类型? - How does TypeScript infer the type of `this`? 为什么打字稿函数参数类型推断失败? - why does typescript function parameter type infer failed? 为什么 TypeScript 会为具有数组类型联合的数组项推断此类型? - Why does TypeScript infer this type for the array item with a union of array types? Typescript 映射返回类型:交集减少为从不,交集属性冲突 - Typescript mapped return type: intersection reduced to never, conflicting intersection property 为什么infer从函数的交集中选择最后一个function的类型呢? - Why does infer choose the type from the last function from the intersection of functions? 尽管 IDE 正确推断了类型,为什么 Typescript 不推断泛型类型保护的类型? - Why does Typescript not infer the type for a generic type guard although the IDE infers the type correctly? 为什么TypeScript无法推断递归函数的类型 - Why TypeScript can not infer type of recursive function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM