Uczymy się utility types Extract, Exclude, uczymy się więcej o never oraz piszemy sami te utility types. Ciekawa lekcja, zaczynajmy.

Zanim zrozumiemy Extract i Exclude zobaczmy jak działa never w TS. Najpierw coś bardzo prostego, na pewno to znamy:

function foo() :never {
    throw new Error("Error");
 }

Ok, kolejna rzecz, która nie powinna nas dziwić:

let empArr = [];
type someT1 = typeof empArr;

//type someT1 = never[]

A teraz coś już naprawdę dziwnego, czyli unia typów razem z never:

type str123 = string | never;
//type str123 = string

Never jest ignorowane. Ok, zobaczmy sobie Extract i Exclude. Najpierw Exclude:

type T0 = Exclude<"a" | "b" | "c", "a">;

// type T0 = "b" | "c"

Jak widać zwróciło nam to po prawej, ale z wywalonymi rzeczami po lewej. Ok, Extract:

type T01 = Extract<"a" | "b" | "c", "a" | "f">;

//type T01 = "a"

Wyciągnęło nam te rzeczy po prawej, które występują po lewej. Brak f nie robi tu żadnego problemu, jak widać.

Dobra, ale jak to wszystko działa? Ano tak:

type T0 = Exclude<"a" | "b" | "c", "a">;

//type Exclude<T, U> = T extends U ? never : T

// type T0 = "b" | "c"

Exclude przyjmuje 2 typy. Sprawdza, czy T zawiera się w U, jeżeli tak, zwraca never, które jest ignorowane, jeżeli nie zwraca T. Ternary operator powinniśmy ogarniać.

Czyli de facto takie coś dostaliśmy:

type T0 = Exclude<"a" | "b" | "c", "a">;

//type Exclude<T, U> = T extends U ? never : T

// type T0 =  never| "b" | "c"

Ale never jest ignorowane. A jak działa Extract?

type T01 = Extract<"a" | "b" | "c", "a" | "f">;

//type Extract<T, U> = T extends U ? T : never

//type T01 = "a"

Odwrotność Exclude. Sprawdza, czy T jest w U, jak jest, zwraca T, jak nie ma, zwraca never, które jest ignorowane.

Sami możemy sobie napisać MyExtract i MyExclude. W sumie wystarczy odkomentować i dodać My do nazwy… Najważniejsze, abyśmy rozumieli dlaczego tak się dzieje.

Ok, jeżeli nie ogarniamy conditional types i ternary operatora to tu jest przykład z docsów TSa:

interface Animal {
  live(): void;
}
interface Dog extends Animal {
  woof(): void;
}
 
type Example1 = Dog extends Animal ? number : string;
//number

Proste. Spełnia warunek – number, nie spełnia – string. Tam po prostu mamy typ generyczny, never które jest ignorowane, unię, no może trochę to w głowie zamieszać, ale aż tak trudne chyba nie jest.

Po prostu musimy to sobie w głowie ułożyć. Do następnego razu!