Execute Program

Everyday TypeScript: Pick and Omit

Welcome to the Pick and Omit lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • Sometimes we want a type with only some of the properties from another type. The TypeScript language designers could have added a special language feature to do that, but they didn't. Instead, they gave us two utility types. These are regular TypeScript types, defined using regular TypeScript code. That code just happens to come with the TypeScript compiler.

  • The Pick type lets us "pick" one or more properties from an object type, creating a new object type with only those properties.

  • >
    type User = {
    name: string
    email: string
    age: number
    };

    type NameOnly = Pick<User, 'name'>;

    const amir: NameOnly = {name: 'Amir'};
    amir;
    Result:
    {name: 'Amir'}Pass Icon
  • >
    type User = {
    name: string
    email: string
    age: number
    };

    type NameOnly = Pick<User, 'name'>;

    const amir: NameOnly = {name: 'Amir', email: 'amir@example.com'};
    amir;
    Result:
    type error: Object literal may only specify known properties, and 'email' does not exist in type 'NameOnly'.Pass Icon
  • What if we want to pick multiple properties? It would be reasonable to guess that we'd do it with Pick<User, 'name', 'email'>. However, that's not possible in TypeScript: generic types must take a fixed number of type parameters. Instead, we pick multiple object properties via a union of the properties' names.

  • >
    type User = {
    name: string
    email: string
    age: number
    };

    type NameAndEmail = Pick<User, 'name' | 'email'>;

    const amir: NameAndEmail = {name: 'Amir'};
    amir;
    Result:
    type error: Property 'email' is missing in type '{ name: string; }' but required in type 'NameAndEmail'.Pass Icon
  • >
    type User = {
    name: string
    email: string
    age: number
    };

    type NameAndEmail = Pick<User, 'name' | 'email'>;

    const amir: NameAndEmail = {name: 'Amir', email: 'amir@example.com'};
    amir;
    Result:
    {name: 'Amir', email: 'amir@example.com'}Pass Icon
  • Here's an example of how we might use this. Suppose that we have a web application with a JSON API. The API is for a forum system: users can register accounts, create threads, and post comments in the threads. We define large, complex types for the concepts in the API, like User, Thread, Comment, etc.

  • Individual API endpoints will only need to include small pieces of those objects. The account page will need most of the user's properties, but other pages will only need a few user properties. When showing an entire thread, we'll need a lot of details about the thread and its comments. But when showing a list of many threads, we'll only need the thread's subject and the number of comments in it.

  • We can use Pick to define these API endpoint types. When showing the thread list, we can pick out only the properties that we need: Pick<Thread, 'subject' | 'commentCount'>. By using Pick, we avoid repeatedly defining separate thread types that duplicate the same properties over and over again.

  • The Omit type is the opposite of Pick. Whereas Pick selects some properties to include, Omit includes all properties except the ones we specify.

  • >
    type User = {
    name: string
    email: string
    age: number
    };

    type NameOnly = Omit<User, 'email' | 'age'>;

    const amir1: {name: string} = {name: 'Amir'};
    const amir2: NameOnly = amir1;
    amir2;
    Result:
    {name: 'Amir'}Pass Icon
  • >
    type User = {
    name: string
    email: string
    age: number
    };

    // These two types are equivalent!
    type NameOnly1 = Pick<User, 'name'>;
    type NameOnly2 = Omit<User, 'email' | 'age'>;

    const amir1: {name: string} = {name: 'Amir'};
    const amir2: NameOnly1 = amir1;
    const amir3: NameOnly2 = amir1;
    amir3;
    Result:
    {name: 'Amir'}Pass Icon
  • >
    type User = {
    name: string
    email: string
    age: number
    };

    type NameOnly = Pick<User, 'name'>;
    type NameAndEmail = Omit<User, 'age'>;

    const amir1: {name: string} = {name: 'Amir'};
    const amir2: NameOnly = amir1;
    const amir3: NameAndEmail = amir1;
    amir3;
    Result:
    type error: Property 'email' is missing in type '{ name: string; }' but required in type 'NameAndEmail'.Pass Icon
  • Here's a code problem:

    The code below defines an Album type. We want to build a new type that contains an album's numerical statistics, but not its name. Fill in the Pick type to select only the copiesSold and releaseYear properties.

    type Album = {
    name: string
    copiesSold: number
    releaseYear: number
    };
    type AlbumStats = Pick<Album, 'copiesSold' | 'releaseYear'>;
    const stats: AlbumStats = {
    copiesSold: 24400000,
    releaseYear: 1973,
    };
    stats;
    Goal:
    {copiesSold: 24400000, releaseYear: 1973}
    Yours:
    {copiesSold: 24400000, releaseYear: 1973}Pass Icon