在打字稿中,为什么 | and & 运算符在函数类型上使用时会翻转其含义吗?

Dan*_*lan 5 types declare typescript

在这段代码中,example1让example2我感到困惑:

type F1 = (a: string, b:string) => void;
type F2 = (a: number, b:number) => void;

//   re: example 1 and 2: 
//   After the =, | means "or" and & means "and"
//   Before the =, & means "or" and | means "and"
const example1: F1 & F2 = (a: string | number, b: string | number) => {}
example1("Hello", "World")
example1(1, 2)
// example1("Hello", 2) // Error! number is not assignable to parameter of type string... (and vice versa)

const example2: F1 | F2 = (a: string | number, b: string | number) => {}
// example2("Hello", "World") // Error! Argument of type string is not assignable to parameter of type never
// example2(1, 2) // Error! Argument of type number is not assignable to parameter of type never
// example2("Hello", 2) // Error! Argument of type string is not assignable to parameter of type never

//   re: example 3,4,5:
//   Before the =, | means "or"
// const example3: number | string = true // Error! Type Boolean is not assignable to type number | string
const example4: number | string = 1
const example5: number | string = "foo"

//   re: example 6,7
//   Before the =, & means "and"
// const example6: {a: string} & {b: number} = {a: "foo"} // Error! Type '{ a: string; }' is not assignable to type '{ a: string; } & { b: number; }'. 
// Property 'b' is missing in type '{ a: string; }' but required in type '{ b: number; }'.
const example7: {a: string} & {b: number} = {a: "foo", b: 5}
Run Code Online (Sandbox Code Playgroud)

对我来说,example1and example2(在 之前=)中的运算符的行为方式与其他运算符相反。以下是我期望这些示例的工作方式:

const example1: F1 & F2 = (a: string | number, b: string | number) => {}
// example2("Hello", "World") // Error! Argument of type string is not assignable to parameter of type never
// example2(1, 2) // Error! Argument of type number is not assignable to parameter of type never
// example2("Hello", 2) // Error! Argument of type string is not assignable to parameter of type never

const example2: F1 | F2 = (a: string | number, b: string | number) => {}
example1("Hello", "World")
example1(1, 2)
// example1("Hello", 2) // Error! number is not assignable to parameter of type string... (and vice versa)
Run Code Online (Sandbox Code Playgroud)

example1如果甚至没有编译,它对我来说也有意义,因为“字符串类型!==数字类型”。

为什么这没有按预期工作?

Sil*_*olo 2

有了这些类型,

type F1 = (a: string, b:string) => void;
type F2 = (a: number, b:number) => void;
Run Code Online (Sandbox Code Playgroud)

以及此声明example1,

const example1: F1 & F2 = (a: string | number, b: string | number) => {}
Run Code Online (Sandbox Code Playgroud)

example1已声明 type F1 & F2,因此它既可以作为两个字符串的函数调用,也可以作为两个数字的函数调用。但不能混合使用这两个参数来调用它。您分配给它的函数值可以,但F1 & F2严格来说是 的超类型(a: string | number, b: string | number) => void,因此当我们分配给具有静态超类型的变量时,我们会丢失信息,就像将数字分配3给类型的变量unknown会丢失信息一样。

const example2: F1 | F2 = (a: string | number, b: string | number) => {}
Run Code Online (Sandbox Code Playgroud)

的类型是可以使用参数example2调用的函数或可以使用参数调用的函数的类型。您分配给它的函数可以用其中任何一个来调用,因此分配没问题。stringnumber

但我们永远无法调用这个函数。完全没有。我们必须向它传递两个参数,这些参数与F1和F2签名兼容。F1Expectsstring和F2Expects number,所以我们需要传递一些既是字符串又是数字的东西,即string & number。是string & number,never空类型。

|在第二个函数中变成 an的原因&是由于一个叫做方差的小东西。函数参数是逆变的,因此((a: A1) => B1) & ((a: A2) => B2)is 等于(a: A1 | A2) => B1 & B2且((a: A1) => B1) | ((a: A2) => B2)等于(a: A1 & A2) => B1 | B2。您可以阅读该维基百科页面以获取有关其背后数学原理的更多详细信息,或者写出“与”和“或”类型的含义并遵循您的直觉。

  • 当您分配给交集类型的变量时,分配给它的内容必须与这两种类型兼容。当您“使用”变量时,您可以自行决定将其视为任一变量。对于工会来说,情况恰恰相反。您可以自行决定分配给它,但是当您使用它时,您必须考虑这两种可能性。这是控制反转。在你的“example1”中,你正在谈论使用变量(即向你做出承诺的变量),而在“example6”中你正在谈论赋值(即你向变量做出承诺)。 (2认同)