never

never 是一种永不存在的值类型,它是一种底部类型。never 类型通常不是主动声明的,而是在意外情况下产生的,属于异常场景下的类型。

由于 never 是底部类型,是所有类型的子类型,因此它的值可以赋给所有类型的变量(但没有任何意义)。示例代码如下。

let a: never;
let boolean1: boolean = a;
let number1: number = a;
let string1: string = a;
let function1: () => void = a;
let object1: { x: number } = a;
let array1: number[] = a;

由于 never 是一种永不存在的值类型,因此给 never 类型的变量赋任何值都会引起编译错误。示例代码如下。

let a: never;
//编译错误:不能将类型"number"分配给类型"never"。ts(2322)
a = 1;
//编译错误:不能将类型"string"分配给类型"never"。ts(2322)
a = "hello";
//编译错误:不能将类型"boolean"分配给类型"never"。ts(2322)
a = true;
//编译错误:不能将类型"number"分配给类型"never"。ts(2322)
a = [1, 2, 3];
//编译错误:不能将类型"() => void"分配给类型"never"。ts(2322)
a = function () { console.log("hello") };
//编译错误:不能将类型"string"分配给类型"never"。ts(2322)
a = { x: "aa", y: 1 }

never 类型通常是由代码中的错误引起的,不需要显式使用,它通常在以下情景下自动产生。

  • 使用了错误的类型判断语句。例如,以下代码声明了一个 unknown 类型的变量,并指定其值为字符串 “hello world!”,但接下来的判断语句存在错误,需要该变量既为 number 类型又为 string 类型才能执行后续代码。由于这种类型是不存在的,因此该变量将被判定为 never 类型,引起编译错误。

    const uncertain: unknown = 'Hello world!';
    
    if (typeof uncertain === 'number' && typeof uncertain === 'string') {
        //编译错误:类型"never"上不存在属性"length"。ts(2339)
        console.log(uncertain.length);
    }
  • 使用了错误的类型操作。例如,以下代码声明了类型别名 A,将 boolean 类型与 string 类型进行了交叉,但是并不存在既为 boolean 类型又为 string 类型的值,因此类型 A 将被判定为 never 类型。

    type A = boolean & string;
    
    //编译错误:不能将类型"number"分配给类型"never"。ts(2322)
    let a: A = 1;
  • 出现无法推断的类型。例如,以下代码声明了一个函数,它拥有一个 string 类型的参数 a,在函数体中首先进行了判断,当 astring 时执行 if 分支,否则执行 else 分支,但 else 编译器无法推断 else 分支下的参数 a 的类型,因此会将其判定为 never 类型,引起编译错误。

    function test(a: string) {
        if (typeof a == "string") {
            console.log(a.length);
        }
        else {
            //编译错误:类型"never"上不存在属性"length"。ts(2339)
            console.log(a.length);
        }
    }