Does Typescript support “subset types”?

后端 未结 7 1681
青春惊慌失措
青春惊慌失措 2020-12-29 02:36

Let\'s say I have an interface:

interface IUser {
  email: string;
  id: number;
  phone: string;
};

Then I have a function that expects a

相关标签:
7条回答
  • 2020-12-29 02:39

    It's worth noting that Partial<T>, as suggested in the accepted answer, makes all fields optional, which is not necessarily what you need.

    If you want to make some fields required (e.g. id and email), you need to combine it with Pick:

    type UserWithOptionalPhone = Pick<IUser, 'id' | 'email'> & Partial<IUser>
    

    Some explanation:

    What Pick does is that it lets you specify a subset of the interface succinctly (without creating a whole new interface repeating the field types, as suggested by other answers), and then lets you use those, and only those fields.

    function hello1(user: Pick<IUser, 'id' | 'email'>) {
    }
    
    hello1({email: '@', id: 1}); //OK
    
    hello1({email: '@'}); //Not OK, id missing
    
    hello1({email: '@', id: 1, phone: '123'}); //Not OK, phone not allowed
    
    

    Now, this is not exactly what we need, as we want to allow, but not require phone. To do that, we "merge" the partial and the "picked" version of our type by creating an intersection type, which then will have id and email as required fields, and everything else as optional – exactly how we wanted it.

    function hello2(user: Pick<IUser, 'id' | 'email'> & Partial<IUser>) {
    }
    
    hello2({email: '@', id: 1}); //OK
    
    hello2({email: '@', id: 1, phone: '123'}); //OK
    
    hello2({email: '@'}); //Not OK, id missing
    
    0 讨论(0)
  • 2020-12-29 02:44

    You can seperate it into different interfaces:

    interface IUser {
        id: number;
    };
    
    interface IUserEmail extends IUser {
        email: string;
    }
    
    interface IUserPhone extends IUser {
        phone: string;
    }
    

    Have your method receive the base IUser interface and then check for the fields you need:

    function doit(user: IUser) {
        if (user.email) {
    
        } else if (user.phone) {
    
        }
    }
    
    0 讨论(0)
  • 2020-12-29 02:52

    If I understand this question correctly, you want something like Flow's $Shape

    So, in one place, you may have something that requires the type

    interface IUser {
      email: string;
      id: number;
      phone: string;
    };
    

    Then, in another place you want a the type with the same type as IUser just with all the fields now optional.

    interface IUserOptional {
      email?: string;
      id?: number;
      phone?: string;
    };
    

    You want a way to auto-generate IUserOptional based on IUser without having to write out the types again.

    Now, I don't think this is possible in Typescript. Things may change in 2.0, but I don't think we're even close to something like this in Typescript yet.

    You could look at a pre-compiler which would generate such code for you before typescript runs, but that doesn't sound like a trivial thing to do.

    With this problem in mind, I can only suggest you try Flow instead. In flow you can just do $Shape<IUser> to generate the type you want programmatically. Of course, Flow differs from Typescript in many big and small ways, so keep that in mind. Flow is not a compiler, so you won't get things like Enums and class implementing interfactes

    0 讨论(0)
  • 2020-12-29 02:54

    proper solution with mapped types:

    updateUser<K extends keyof IUser>(userData: {[P in K]: IUser[P]}) {
        ...
    }
    
    0 讨论(0)
  • 2020-12-29 02:54

    You can declare some or all fields as optional fields.

    interface IUser {
      email: string; // not optional
      id?: number; // optional 
      phone?: string; // optional
    };
    
    0 讨论(0)
  • 2020-12-29 02:58

    Typescript now supports partial types.

    The correct way to create a partial type is:

    type PartialUser = Partial<IUser>;
    
    0 讨论(0)
提交回复
热议问题