'How can I make one property non-optional in a typescript type?
I have this type:
type User = {
id: string;
name?: string;
email?: string;
}
And I would like to construct a similar type with name non optional:
type UserWithName = {
id: string;
name: string;
email?: string;
}
Instead of repeating the type as I did above, how can I construct UserWithName from User with generic utility types?
Required almost does the job but it sets all properties as non-optional, while I just want to set one property.
Solution 1:[1]
If you check the source for the Required type, it's this:
type Required<T> = {
[P in keyof T]-?: T[P]
}
The same syntax can be used to construct a generic type that will give you what you want:
type User = {
id: string
name?: string
email?: string
}
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }
type UserWithName = WithRequired<User, 'name'>
// error: missing name
const user: UserWithName = {
id: '12345',
}
Solution 2:[2]
You can use interfaces instead:
interface User {
id: string;
name?: string;
email?: string;
}
interface UserWithName extends User {
name: string;
}
Now you've built on the User type, but overwritten the name property to be mandatory. Check out this typescript playground - a UserWithName that doesn't have a name property will error.
Solution 3:[3]
I improved on the suggestion made at the top.
type Required<T, K extends keyof T> = T & { [P in K]-?: T[P] }
type WithRequired<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> & Required<T, K>
now you can require multiple properties at once
interface foo {x?: number, y?: String}
WithRequired<foo, 'x' | 'y'>
every property is now required
Solution 4:[4]
I think this is simpler and works pretty well. Given the User type of which you want to make the name property non-optional:
type User = {
id: string;
name?: string;
email?: string;
}
To create a named interface:
interface UserWithName extends User {
name: NonNullable<User['name']>
}
let personWithName1: UserWithName;
To create a named type:
type UserWithName = User & {
name: NonNullable<User['name']>
}
let personWithName2: UserWithName;
To create an anonymous type:
let personWithName3: User & { name: NonNullable<User['name']> }
I like this approach because you don't need to define a new Generic, but you will still benefit from being able to freely change the type of the name property in User, and you will also be shown an error if you ever change the name itself of the name property.
In case you don't fully understand what's going on, I'm using:
The built-in NonNullable utility type.
Lookup types for getting the type of a property.
Intersection types for extending a type (alternative to the
extendskeyword that only works for named interfaces).
Solution 5:[5]
If you need to make a nested property mandatory, an intersection is your friend. Works with both interfaces and types.
// This can be a type too
interface User {
id: string
properties: {
name?: string
email?: string
}
}
// Make properties.name mandatory
type UserWithEmail = User & { properties: { name: string } }
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Vojt?ch Strnad |
| Solution 2 | Seth Lutske |
| Solution 3 | Maix |
| Solution 4 | |
| Solution 5 | Louis Coulet |
