Poznajemy kolejne, w zasadzie dosyć proste utility types – Pick oraz Omit. Znamy tych utilsów już dość dużo, więc raczej z kronikarskiego obowiązku o nich wspominamy.

Ok, taki interfejs mamy:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }

Teraz za pomocą pick chcemy wybrać sobie konkretne pola z tego interfejsu:

type TodoPreview = Pick<Todo9, "title" | "completed">;

//   type TodoPreview = {
//     title: string;
//     completed: boolean;
// }

Jak widać utworzyło nam typ na podstawie tamtego interfejsu, ale brane pod uwagę są tylko te pola, które zaznaczyliśmy. Tak wygląda ten typ w użyciu:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }
   
type TodoPreview = Pick<Todo9, "title" | "completed">;

//   type TodoPreview = {
//     title: string;
//     completed: boolean;
// }

  const todo9: TodoPreview = {
    title: "Clean room",
    completed: false,
  };

Ok, fajnie, ale jak ten pick działa? Cóż, sami sobie możemy go napisać:

type MyPick<T, K extends keyof T> = { [P in K]: T[P]; }

Pierwsze, co bierze, to typ T. Drugie to K, który musi być kluczem/kluczami T. I iteruje po tych kluczach i w ten sposób tworzy obiekt, którego properties to są K. I z typu T bierze T[P] czyli typy tych properties.

Może być ciężko to ogarnąć, może łatwo, z doświadczenia powiem, że po prostu dwa/trzy dni mapowanymi typami się trzeba pobawić i potem się to czyta na totalnym luzie.

Ok, sprawdźmy, czy działa:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }

type MyPick<T, K extends keyof T> = { [P in K]: T[P]; }
   
type TodoPreview = MyPick<Todo9, "title" | "completed">;

//   type TodoPreview = {
//     title: string;
//     completed: boolean;
// }

  const todo9: TodoPreview = {
    title: "Clean room",
    completed: false,
  };
   

Działa, co ma nie działać. Teraz Omit, domyślamy się już pewnie, co robi:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }

type TodoPreview = Omit<Todo9, "completed">;

// type TodoPreview = {
//     title: string;
//     description: string;
// }

const todo9: TodoPreview = {
    title: "Clean room",
    description: "Clean your room!"
  };

Tutaj kazaliśmy mu olać completed. Pytanie jednak, jak ten omit działa:

type MyOmit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }

Ok, za długie, możemy to uprościć:

type MyOmit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }

Dobra, i co my tu mamy? T i K. T to typ, K to coś, co może być kluczem. Potem iterujemy po kluczach tylko właśnie, co my tam robimy?

Mamy Exclude, któremu przekazujemy klucze T i każemy wywalić K. Jak już mamy odsiane z kluczy T wszystkie K to iterujemy i wyciągamy z tych niepominiętych kluczy wartości T[P].

Tu warto zrozumieć, że do omit możemy przekazać klucze, których nie ma w T:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }

type MyOmit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }
type TodoPreview = Omit<Todo9, "completed"| "createdAt"| "fullName">;

// type TodoPreview = {
//     title: string;
//     description: string;
// }

Natomiast wszystko działa bez zarzutu. Bo tak działa exclude:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }

type Excluded = Exclude<keyof Todo9, "completed"| "createdAt"| "fullName">;
//"title" | "description"

Już to tłumaczyliśmy, ale możemy przypomnieć:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }
  
//type Exclude<T, U> = T extends U ? never : T
type Excluded = Exclude<keyof Todo9, "completed"| "createdAt"| "fullName">;
//"title" | "description"

Chyba wszystko jasne. Do kupy zbierając:

interface Todo9 {
    title: string;
    description: string;
    completed: boolean;
  }
  
//type Exclude<T, U> = T extends U ? never : T
type Excluded = Exclude<keyof Todo9, "completed"| "createdAt"| "fullName">;
//"title" | "description"

type MyOmit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }
type TodoPreview = Omit<Todo9, "completed"| "createdAt"| "fullName">;

// type TodoPreview = {
//     title: string;
//     description: string;
// }

Extract działa odwrotnie, była o tym lekcja. Koniec końców, chodzi o to, abyśmy wiedzieli, jak te util types (Exclude, Extract, Omit, Pick) działają, choć umiejętność napisania ich samemu czy też zrozumienia o co chodzi po szybkim zastanowieniu się, to znak, że idziemy w dobrym kierunku i niedługo będziemy obeznani w TS.

Do następnego razu!