简体   繁体   中英

Understanding the never type in TypeScript 2

While taking a look at what's new in TypeScript 2.0, I've found the never type. According to the docs, it seems like it's a clever way of setting the type of functions that never return.

Now, if I read everything correctly, then never can be assignable to every type, but only never can be assigned to never . So, while writing a small test in VS Code, I've ended up with the following:

function generateError(){
    throw new Error("oops");
}
function do(option: "opt1" | "opt2"){
    if(option === "opt1") return true;
    if(option === "opt2 ) return false;
    generateError();
}
let res = do("blah");

Well, what's the expected type of res ? According to the compiler, it's string | undefined string | undefined (which makes sense, though I must say I expected string ). I guess I'm not seeing the point of having a new type just to represent functions that never return. Do we really need this concept? IS this just a compiler thing that helps it have better flow analysis?

You can use never to ensure that you won't miss a function contract.

function forever(): never {
    while (true) {
        break; // Error because you can't leave the function.
    }
}

enum Values {
    A,
    B
}

function choose(value: Values) {
    switch (value) {
        case Values.A: return "A";
    }

    let x: never = value; // Error because B is not a case in switch.
}

Never is information that this particular part shouldn't be reachable. For example in this code

function do(): never {
    while (true) {}
}

you have an infinite loop and we don't want to iterate infinite loop. Simply as that.

But a real question is how can it be useful for us? It might be helpful for instance while creating more advanced types to point what they are not

for example, let's declare our own NonNullable type:

type NonNullable<T> = T extends null | undefined ? never : T;

Here we are checking if T is null or undefined. If it is then we are pointing that it should never happen. Then while using this type:

let value: NonNullable<string>;
value = "Test";
value = null; // error

Functions that never (ever) return and functions that may throw are not quite the same thing.

For example:

function foo(option: "opt1" | "opt2"): string | undefined {
  if (option === "opt1") return true;
  if (option === "opt2") return false;
  throw new Error("unknown option");
}

function bar(option: "opt1" | "opt2"): never {
  while (true) {
    doOption(option);
  }
}

The first one may (or may not) return, but it can, so the return type cannot be never . If it can return a value, the return type obviously is not never .

The second one will never return. Not a value, not undefined, nothing. There is no case in which it will return, so the type can be never .

There are some functions that look like #2 but actually fall within #1, typically when throw is involved or if you're using process.exit in node (which can't return as it kills the process).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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