Reputation: 84912
I'm attempting to make a mapped type which takes all properties with a certain type (in my case, arrays) and makes them optional. But any other properties (non-arrays) are left untouched. I know the optional modifier can be used when defining a mapped type, as in:
type MyPartial<T> = {
[key in keyof T]?: T[key]
}
But i'm not sure if the syntax allows this to be combined with conditions. I can get pretty close with the following:
type OptionalArrays<T> = {
[key in keyof T]: T[key] extends Array<any> ? T[key] | undefined : T[key]
}
interface Example {
foo: string[];
bar: number;
};
type Example2 = OptionalArrays<Example>;
Problem is that this results in explicit undefined
s, not implicit ones, so while it will behave as i want for these two cases:
const value1: Example2 = {
foo: [],
bar: 3,
}
const value2: Example2 = {
foo: undefined,
bar: 3,
}
It will give an unwanted error that foo
is missing for this one:
const value3: Example2 = {
bar: 3,
}
Is it possible to add the optional modifier (?
) in a mapped type, but only when a certain condition is met?
Upvotes: 9
Views: 3452
Reputation: 187014
It's possible there's a more elegant way, but it's not too awkward to intersect two mapped types instead. One for the optional properties, and one for the required ones.
For example:
type OptionalArrays<T> = {
[key in keyof T as T[key] extends Array<any> ? key : never]?: T[key]
} & {
[key in keyof T as T[key] extends Array<any> ? never : key]: T[key]
}
Note the as
in the key portion of the mapped type. This let's you transform the key type. In this case we either use the actual key if we want to keep it, or transform it to never
if we dont.
Or perhaps this version which is a similar, but arguably slightly less cryptic.
type ArrayKeys<T> = {
[key in keyof T]: T[key] extends Array<any> ? key : never
}[keyof T]
type OptionalArrays<T> =
Omit<T, ArrayKeys<T>> & // get one type without array keys
Partial<Pick<T, ArrayKeys<T>>> // get one type with array keys as optional
Upvotes: 15