Never type

The typescript never type represents the type of a value that never exists and can only be assigned to never.

Crossing any type with never yields never:

type T1 = number & never;   // never
type T2 = string & never;   // never
Copy the code

If type T = T1 & T2, then a value of type T can be assigned to a variable of type T1 or T2 (similar to class inheritance). So if crossed with never, then a value of type T can be assigned to a variable of type never, and then T can only be never.

Any type combined with never is not affected:

type T1 = number | never;   // number
type T2 = string | never;   // string
Copy the code

Understanding is similar with the cross type: if the type T = T1 | T2, T1 and T2 value of type can be assigned to variables of type T. Since the never type can be assigned to any variable, there is no effect on the union type.

keyof

Typescript’s keyof keyword maps a type to a combined type of all its member names. Here’s an example from the typescript website:

interface Person {
    name: string;
    age: number;
    location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string
Copy the code

Keyof actually gives us a way to manipulate union types. Combined with typescript’s other features, such as type mapping and index types, we can easily switch between object types and union types to find the most appropriate type attribution for more variables in our project.

application

Diff Type

Let’s look at an example from here:

type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never })[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
Copy the code

The next line, without further explanation, removes members of type T whose names are in K. The above line of code is particularly interesting and difficult to understand. What it does is to remove the strings contained in U from T for the two string literals T and U:

type A = Diff<"a" | "b" | "c"."a">;        // "b" | "c"
type B = Diff<"a" | "b" | "c"."b" | "d">;  // "a" | "c"
Copy the code

How does it do it? Let’s start with the first part:

type FirstHalf<T extends string, U extends string> = { [P in T]: P } & { [P in U]: never }
type C = FirstHalf<"a" | "b" | "c"."b" | "d">; 
/ / {
// "a": "a",
// "b": "b",
// "c": "c"
/ /} and {
// "b": never,
// "d": never
// }
Copy the code

Let’s cross type C with each member:

type C = {
    "a": "a"."b": "b" & never,
    "c": "c"."d": never
}
Copy the code

The result of any type crossed with never is never, so

type C = {
    "a": "a"."b": never,
    "c": "c"."d": never
}

Copy the code

Let’s look at the Diff type:


type B = Diff<"a" | "b" | "c"."b" | "d">;

       = {
            "a": "a"."b": never,
            "c": "c"."d": never
         }["a" | "b" | "c"];

       = "a" | never | "c";

       = "a" | "c";
Copy the code

In this way the purpose described above is achieved.

Remove all never members

We are trying to remove all members of type Never from an object type. It can be done like this:

type OmitNever<T> = Pick<T, {[P in keyof T]: T[P] extends never ? never : P}[keyof T]>;

type T = {
    a: string,
    b: never,
    c: string,}type T1 = OmitNever<T>;     // { a: string, c: string }
Copy the code

The principle is similar to the first example. We’re trying to pick out the names of all the non-never members in T, pick them out of T. So let’s create an object type:

type OmitNeverHalf<T> = {[P in keyof T]: T[P] extends never ? never : P}

type TT = OmitNeverHalf<T>;     
/ / {
// "a": "a",
// "b": never,
// "c": "c"
// }
Copy the code

Then we use keyof T to make an index type, change the object type to union type, and we get the member names we want.