This question is kind of hard to express for me which might be the reason why I did not find anything on researching.
I'm trying to get a type that ends up with having arrays only. I always want to end up with "one-level" arrays (If arrays of arrays are given, I don't care about the output. This does not happen in my case). So the wanted outcome is the following:
type T1 = ForceArray<string> // wanted: string[]
type T2 = ForceArray<string[]> // wanted: string[]
I can do this with the following code:
type ForceArray<T> = NonNullable<T> extends Array<any> ? T : Array<T>
NonNullable is used here because in the actual use case, ForceArray<T> is used on interface properties which can be optional. This means that T can be undefined, or in other words T = U | undefined where U is the actual type (U = string or U = string[] in the shown examples).
The type definition above works well, except for union types.
type T3 = ForceArray<string | string[]> // shows (string | string[])[] but I want string[]
Also other union types should be converted to arrays which does not work either:
type T4 = ForceArray<number | string[]> // wanted: number[] | string[]
type T5 = ForceArray<number[] | string> // wanted: number[] | string[]
In the typescript docs they kind of relate to this by talking about Distributive conditional types. But as far as I understand the docs, my code should just work. So obviously I didn't understand the docs correctly.
Is there a way to get the expected behaviour?
Additional information:
The actual code that produces the output is extremely simple which is the reason why I beleave there should be a solution:
const forceArray = (value) => Array.isArray(value) ? value : [value];
This function is used in a context like this, where obj can implement any interface (with optional properties):
const convertObjectValuesToArray = (obj) => {
const newObj = {};
for (const key of obj) {
newObj[key] = forceArray(obj[key]);
}
return newObj;
}