为什么打字稿会推断“从不”而不是交点类型?

时间:2019-07-25 08:59:12

标签: typescript

给出以下示例:

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

打字稿显示以下错误:

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'.

这是清楚易懂的。但是,如果我像这样向接口添加联合类型:

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

我收到以下错误:

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

如果我使用交集类型string & number & MultiType,Typescript会接受它,但也接受never

这似乎与我不一致。这可能是错误吗?

2 个答案:

答案 0 :(得分:3)

string & number等效于neverstring & number & (MultiType | null)也等效。没有同时为stringnumber的值,因此没有值满足string & numberstring & number & AnythingElse

现在后面的explicitly reduces to never是因为它包含这些等效于never类型的并集,这样离开真是很难看。具体来说,编译器将交集分布在并集上,因此

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

成为

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

该类型对人们的启发并不特别,因此编译器会检查每个联合组成部分是否等同于never并将该类型简化为

never | never | never

这只是

never

如您所见。


那么为什么string & number本身不会立即减少为never?最初的想法很不错,因为string is not assignable to numberstring is not assignable to never更具启发性,它可以帮助人们理解代码中的错误来源。

不幸的是,就可为其赋值和从中赋值而言,string & numbernever等价于string | number is not assignable to never,但是在TS3.5及以下版本中,编译器并不总是处理它们相同,这令人困惑。

因此,从TS3.6开始,empty intersections like string & number will be reduced to never explicitly看起来像。 TS3.6发布后,您的上述代码在两种情况下都将发挥相同的作用,并且会出现 from helpers.helper_a import * import pandas as pd query_train_data = "select * from train;" df_train_dataset = pd.read_sql(query_train_data, con=engDps1000) query_test_data = "select * from test;" df_test_dataset = pd.read_sql(query_test_data, con=engDps1000) df_train_data = df_train_dataset df_test_data = df_test_dataset data_prep_steps() # This function is defined in helpers_a Error: --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-12-3c88b46f341a> in <module> ----> 1 data_prep_steps() ~\Desktop\Notebooks\helpers\helper_a.py in data_prep_steps() ---> 89 # STEP 1 : CONVERT REQUIRED COLS TO CATEGORIES 90 for df_name in [df_train_data, df_test_data]: 91 data_prep_class = data_prep(df_name) 错误。


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

答案 1 :(得分:1)

两件事:

  • 由于以下所有原因,TypeScript不接受string | number作为value参数的类型是合乎逻辑的:

    • string | number无法分配给string,因为在numberstring的情况下key无法分配给'name' < / p>

    • string | number无法分配给number,因为在stringnumber的情况下key无法分配给'value'(在您的第一个示例中,两个都已经成立了

    • string | number无法分配给MultiType | null,因为在number的情况下string'x' | 'y' | null都不能分配给key'multiType'

但是,由于某些原因,在第一个示例中,TypeScript只是在第一个“错误”情况下停止并给出了此错误(即使实际上已经有两件事出了错)。

在第二种情况下,您可能会看到相同的错误消息,因为类型的不兼容性仍然存在,推断似乎更加深入,并告诉您问题比这更深。至于为什么错误消息是这样格式化的,我真的不知道,我想这将需要更深入地了解编译器在此处的推断方式。最后,编译器是正确的,但错误消息可能会更清楚。


对于第二个问题,the never type文档说:

  

never类型是每种类型的子类型,并且可以分配给每种类型

所以这就是为什么您可以将value指定为never类型并将其分配给data的原因。因为它将永远不会发生