-
-
Save gomezcabo/dff1d95fd1eb354f686d6606a511d7da to your computer and use it in GitHub Desktop.
type RecursiveRequired<T> = Required<{ | |
[P in keyof T]: T[P] extends object | undefined ? RecursiveRequired<Required<T[P]>> : T[P]; | |
}>; | |
type ExampleType = { | |
a?: number; | |
b: number; | |
c?: { | |
d?: { | |
e?: number; | |
f: boolean; | |
g?: { | |
h: string | |
} | |
} | |
} | |
} | |
type ExampleTypeRequired = RecursiveRequired<ExampleType> | |
const data: ExampleTypeRequired = { | |
a: 1, | |
b: 1, | |
c: { | |
d: { | |
e: 1, | |
f: false, | |
g: { | |
h: 'hello' | |
} | |
} | |
} | |
} |
You’re welcome!
appreciate it
Thanks! 🤗
thanks for your gist!!!
Inspired by you, implemented using the -?
operator.
type RequiredDeep<T> = {
[P in keyof T]-?: T[P] extends object | undefined ? RequiredDeep<T[P]> : T[P];
};
Supports function
export type DeepRequired<T> = T extends Function
? T
: T extends object
? {
[P in keyof T]-?: DeepRequired<T[P]>
}
: T
was looking for this, thanks @gomezcabo!
@kennarddh Is there any way to make this work with a generic type that references its own type? For example:
interface TableColumn<T> {
mappedProperty: keyof T;
label?: string;
}
This interface could represent a table column definition that requires the name of a valid property from the row item schema. Currently, this produces a type that makes mappedProperty
of type DeepRequired<keyof T>
when applied to TableColumn<T>
instead of leaving it as keyof T
.
This creates a problem when attempting to create objects that satisfy this in a mapping function:
function normalizeDefs<T extends object>(defs: TableColumn<T>[]): DeepRequired<TableColumn<T>>[] {
return defs.map(d => ({
label: d.mappedProperty.toString().toUpperCase(),
...d
}));
}
TS playground link.
Good catch! What about updating the DeepRequired type?
type DeepRequired<T> = T extends Function
? T
: T extends object
? Required<{
[K in keyof T]-?: T[K] extends PropertyKey ? T[K] : DeepRequired<T[K]>;
}>
: T;
Now, if T[K]
is not an object, but a PropertyKey
(e.g. string | number | symbol
) we use that type (keyof T
in your case).
What do you think @C-Duxbury ?
Link to playground here.
@gomezcabo Yes, that does the trick nicely! Thanks!
Just for my own academic curiosity, do you know why the TS compiler was saying 'string' is not assignable to type 'DeepRequired<keyof T>'
? It seems like they should be compatible.
@gomezcabo Yes, that does the trick nicely! Thanks!
Just for my own academic curiosity, do you know why the TS compiler was saying
'string' is not assignable to type 'DeepRequired<keyof T>'
? It seems like they should be compatible.
I guess when passing down the keyof T
to the generic, this one could be string
, number
or symbol
and that's where the Type was failing. No way to determine which one of those. That's why the fix was to include the check T[K] extends PropertyKey
(PropertyKey === string | number | symbol
).
This also works with arrays, which comes in handy in my case.
Thanks for the gist!