2

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

lonewarrior556
  • 3,917
  • 2
  • 26
  • 55

2 Answers2

1

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

kelsny
  • 23,009
  • 3
  • 19
  • 48
  • 2
    The check `{} extends Pick` should work to determine if `K` is an optional property regardless of `undefined`, since `{} extends {foo?: string}` is true but `{} extends {foo: string | undefined}` is false. Note that `undefined extends T[K]` is true for `T[K]` being `unknown` and `any`, so you might want to list that as a caveat (or [edit] to use `{} extends Pick` instead) – jcalz Mar 16 '22 at 19:07
  • Thank you! I updated my answer accordingly – kelsny Mar 16 '22 at 21:12
1

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>
}
mullin
  • 133
  • 1
  • 6