TypeScript learning notes - Omit

Posted by echo10 on Mon, 07 Mar 2022 09:36:02 +0100

After version 3.5, TypeScript added an omit < T, k > type.
Omit < T, k > types can exclude some attributes from inherited object attributes.

type User = {
  id: string;
  name: string;
  email: string;
};

type UserWithoutEmail = Omit<User, "email">;

// Equivalent to
type UserWithoutEmail = {
  id: string;
  name: string;
};

Omit < T, k > in lib es5. d. TS file is defined as follows:

Construct a type with T attribute other than type K

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Explain this type definition clearly and understand its principle. We can try to implement our own version to restore its functions.

Define omit < T, k > help type

type User = {
  id: string;
  name: string;
  email: string;
};

First, we need to traverse the property names in the User {type. We can use the {keyof} operator to obtain a string set containing all object attributes:

type UserKeys = keyof User;

// Equivalent to
type UserKeys = "id" | "name" | "email";

Then you need to take out some properties from the string collection.
In the | User | type, you need to remove "email" from "Id" | name "| email". You can use exclude < T, u > to do this:

type UserKeysWithoutEmail = Exclude<UserKeys, "email">;

// Equivalent to
type UserKeysWithoutEmail = Exclude<"id" | "name" | "email", "email">;

// Equivalent to
type UserKeysWithoutEmail = "id" | "name";

Exclude < T, u > in lib es5. d. TS file is defined as follows:

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

It uses a condition type and a {never} type. Use | exclude < T, u > to remove those types that match the type of "email" from the set "Id" | name "| email". The only thing that matches the "email" type is itself, so there is only "id" | "name".

Finally, we need to create an object type that contains a subset of User # type attributes. Specifically, to create an object that only contains UserKeysWithoutEmail type, you can use {pick < T, k > to pick out the corresponding attribute name in the User type.

type UserWithoutEmail = Pick<User, UserKeysWithoutEmail>;

// Equivalent to
type UserWithoutEmail = Pick<User, "id" | "name">;

// Equivalent to
type UserWithoutEmail = {
  id: string;
  name: string;
};

Pick < T, k > in lib es5. d. This is defined in the TS file

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Pick < T, k > is a mapping type, which uses the # keyof # operator and an index type # T[P] to obtain the attribute # P in the type object type # t #.
Now, let's integrate the above mentioned , keyof, exclude < T, u > and , pick < T, k > into one type

type UserWithoutEmail = Pick<User, Exclude<keyof User, "email">>;

It is worth noting that this writing can only be applied to the User type we define. Adding a template can make it used elsewhere

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

Now, you can calculate the user without email type

type UserWithoutEmail = Omit<User, "email">;

Because the key of an object can only be a string, a number or a Symbol, we can add a constraint to K

type Omit<T, K extends string | number | symbol> = Pick<T, Exclude<keyof T, K>>;

In this way, it seems a little verbose to directly restrict the extension string | number | symbol. We can use , keyof any , because they are equivalent

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

This implements lib es5. d. Types of omit < T, k > defined in TS

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Disassemble omit < user, "email" >

The following code is the type of "omit < user," email "> disassembled step by step. Try to follow each step and understand how TypeScript calculates the final type

type User = {
  id: string;
  name: string;
  email: string;
};

type UserWithoutEmail = Omit<User, "email">;

//  Equivalent to
type UserWithoutEmail = Pick<User, Exclude<keyof User, "email">>;

//  Equivalent to
type UserWithoutEmail = Pick<User, Exclude<"id" | "name" | "email", "email">>;

//  Equivalent to
type UserWithoutEmail = Pick<
  User,
  | ("id" extends "email" ? never : "id")
  | ("name" extends "email" ? never : "name")
  | ("email" extends "email" ? never : "email")
>;

//  Equivalent to
type UserWithoutEmail = Pick<User, "id" | "name" | never>;

//  Equivalent to
type UserWithoutEmail = Pick<User, "id" | "name">;

//  Equivalent to
type UserWithoutEmail = {
  [P in "id" | "name"]: User[P];
};

// This is equivalent to:
type UserWithoutEmail = {
  id: User["id"];
  name: User["name"];
};

// This is equivalent to:
type UserWithoutEmail = {
  id: string;
  name: string;
};

Reference link
The Omit Helper Type in TypeScript
Omit help type in TypeScript

Topics: Front-end TypeScript ts