'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
extends
keyword 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 |