Does Typescript support "subset types"?

What you want is this

type Subset<T extends U, U> = U;

this makes sure, that U is a subset of T and returns U as a new type. for example:

interface Foo {
 name: string;
 age: number;
}

type Bar = Subset<Foo, {
 name: string;
}>;

you can not add new properties to Bar which are not part of Foo - and you can not alter types in a non-compatible way. this also works recursively on nested objects.


Typescript now supports partial types.

The correct way to create a partial type is:

type PartialUser = Partial<IUser>;

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