I need a Partial<T>
like mapped type that allows me make nullable fields optional. I'm working on typing our ORM and it coerces undefined to nulls on nullable fields.
I'd like to take a type of
interface User {
email: string
name: string | null
}
And make
interface User {
email: string
name?: string | null
}
I tried
type NullablePartial<T> = { [P in keyof T]: T[P] extends null ? T[P] | undefined : T[P] }
But extends null
doesn't work for the union of null
and another type (and I'd ideally like it to work with more than strings). And the difference between optional fields and undefined values is important.
What can I do?
Making a type function turn just some properties optional or non-optional is actually quite annoying in TypeScript, because there's no simple way to describe it. The easiest way I can think to do it is using multiple mapped types with an intersection. Here's something that should work as long as the type you pass in doesn't have an index signature (it gets more complicated):
type NullablePartial<
T,
NK extends keyof T = { [K in keyof T]: null extends T[K] ? K : never }[keyof T],
NP = Partial<Pick<T, NK>> & Pick<T, Exclude<keyof T, NK>>
> = { [K in keyof NP]: NP[K] }
Note that I'm using Pick
and Exclude
to carve up T
into the nullable properties and the non-nullable properties, doing different things to each of them, then intersecting them back together. Determining nullable properties has to do with checking if null extends T[K]
(i.e., "can I assign null
to this property"), not the reverse (i.e., "can I assign this property to a variable of type null
").
I'm also using generic parameter defaults to make the type manipulation more concise (so I only have to determine the nullable property key names once as NP
) and to make the final output type more aesthetically pleasing (by doing one final mapped type at the end I get a type that is not represented as an intersection).
Let's try it on:
interface User {
email: string
name: string | null
}
type NPUser = NullablePartial<User>;
// type NPUser = {
// name?: string | null | undefined;
// email: string;
// }
Looks reasonable to me... is that close to what you wanted? Good luck.
Let's start by finding which properties are nullable.
type NullablePropertyOf<T> = {
[K in keyof T]: null extends T[K]
? K
: never
}[keyof T]
We need to modify nullable properties, but leave the rest unaffected. In order to ignore the non-nullable ones in our transformation, we will need the well-known Omit
helper.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
Our final product is created by merging the weak, partial version of the entire T
with the properties that were supposed to remain unaffected.
type NullablePartial<T> = Partial<T> & Omit<T, NullablePropertyOf<T>>;
There is, of course, more than one way to do it. In this example, I'm optionalizing everything and requiring what's required. You could take the opposite approach: pick the non-nullable fields first, and optionalize the nullable ones, then merge both types. Whatever suits you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With