TypeScript: Transforming optional properties to required properties that may be undefined

Kevin Ring
Terria
Published in
2 min readMar 20, 2019

Here’s a handy TypeScript generic for transforming a type with optional properties into a new type where all the properties exist, but the optional properties on the original type may be undefined on the new one. First, a little background…

problem by Gregor Cresnar from the Noun Project

When defining a type in TypeScript, we can specify that a property is optional with a question mark after the name:

Or we can specify that a property may be undefined:

These two interfaces seem nearly identical. In either case, accessing the property foo may return the value undefined. They are subtly different, though, as evidenced by the fact that TypeScript won’t actually allow us to assign InterfaceWithOptional to InterfaceWithUndefined:

TypeScript reports:

Type ‘InterfaceWithOptional’ is not assignable to type ‘InterfaceWithUndefined’.
Property ‘foo’ is optional in type ‘InterfaceWithOptional’ but required in type ‘InterfaceWithUndefined’.ts(2322)

This makes sense if you consider that foo?: number means that the property foo may not exist at all on instances of InterfaceWithOptional. 'foo' in instance for example, will return false.

Transforming

We can use conditional types to automatically transform InterfaceWithOptional into InterfaceWithUndefined. It looks like this:

We can use this to transform a type with a mix of required and optional properties into a new type where all the properties are required but some of them may be undefined:

This works by mapping over Required<T>, a version of our original type where all the optional properties have been replaced by required ones. Then, for each property, we use a conditional to check whether a type created by extracting just that one property extends (read: is assignable to) the same type where the property has been made required.

If the property was already required, we’re effectively testing if a type is assignable to itself. It is, so the type of the property in our “output” is exactly the same as the “input”. If the property was optional on input, however, the extends test will fail, much like howInterfaceWithOptional was not assignable to InterfaceWithUndefined above. In that case, we’ll add an | undefined to the property’s type.

While this is all a bit esoteric and there probably aren’t too many situations where you need to do this transformation, I hope this post saves you some time if you do need to work out how to do it.

--

--