typescript exclude optional fields from type (deep)

Viewed 216

This is an expansion of this questions: typescript exclude optional fields from type

If I have a type like this:

type MyType = {
  thingOne: { id: string; name?: string };
  thingTwo?: { id: string; name?: string };
};

I want to be able to create a type that only has the required fields in the object, aswell as anything nested inside of it.

ie.

type MyRequireds = NonOptional<MyType>;
/*
{
  thingOne: { id: string };
};
*/

is it possible?

TSPlayground link with shallow NonOptional TypeScripPlayground

2 Answers

With this recursive type:

type OnlyRequired<T> =
    T extends object
        ? { [K in keyof T as {} extends Pick<T, K> ? never : K]: OnlyRequired<T[K]> }
        : T;

If T is a primitive, we don't need to do anything. However if it is an object we have to go through each key and get only the ones that can't be optional (credit to @jcalz) and then recursively gets only required keys of T[K].

Note that functions need a little special handling and checking for undefined is not exhaustive or the best way to check for optional properties but it does work for your example.

Playground

For omitting optional fields from nested objects

export type OmitOptionalNested<T> = { [P in keyof Required<T> as Pick<T, P> extends Required<Pick<T, P>> ? P : never]: 
        T[P] extends (infer U)[] ? OmitOptionalNested<U>[] :
        T[P] extends object ? OmitOptionalNested<T[P]> :
        T[P] 
}

Playground

Note: linters can sometimes only show one red squiggle at a time but it will break on compile saving you headaches

For the future, you can make almost any type a nested type by using the bellow template

type Basic<T> = { [P in keyof T]: T[P] }

type Nested<T> = { [P in keyof T]: // some logic to limit keys in object 
        T[P] extends (infer U)[] ? Nested<U>[] : // handles array
        T[P] extends object ? Nested<T[P]> : // handles objects
        T[P] // Same as in Basic<T>
}
Related