如何从联合创建类型数组类型
How to create types array types from unions
这个问题对我来说有点难以表达,这可能是我没有找到任何研究的原因。
我正在尝试获得一个最终只有数组的类型。我总是想以“一级”数组结束(如果给出数组的数组,我不关心输出。这在我的情况下不会发生)。所以想要的结果如下:
type T1 = ForceArray<string> // wanted: string[]
type T2 = ForceArray<string[]> // wanted: string[]
我可以用下面的代码来做到这一点:
type ForceArray<T> = NonNullable<T> extends Array<any> ? T : Array<T>
这里使用NonNullable
是因为在实际用例中,ForceArray<T>
用于可选的接口属性。这意味着 T
可以是 undefined
,或者换句话说 T = U | undefined
,其中 U
是实际类型(显示的 U = string
或 U = string[]
示例)。
上面的类型定义很好用,联合类型除外。
type T3 = ForceArray<string | string[]> // shows (string | string[])[] but I want string[]
其他联合类型也应转换为数组,这也不起作用:
type T4 = ForceArray<number | string[]> // wanted: number[] | string[]
type T5 = ForceArray<number[] | string> // wanted: number[] | string[]
在打字稿文档中,他们通过谈论 Distributive conditional types 与此相关。但据我了解文档,我的代码应该可以正常工作。所以很明显我没有正确理解文档。
有没有办法获得预期的行为?
附加信息:
产生输出的实际代码非常简单,这就是我认为应该有解决方案的原因:
const forceArray = (value) => Array.isArray(value) ? value : [value];
此函数在这样的上下文中使用,其中 obj
可以实现任何接口(具有可选属性):
const convertObjectValuesToArray = (obj) => {
const newObj = {};
for (const key of obj) {
newObj[key] = forceArray(obj[key]);
}
return newObj;
}
NonNullable
似乎是这里的问题,因为它禁用了类似于括号 []
的通用类型的可分配性。我们可以删除它并用它代替 returned 类型。
type ToArray<Type> = Type extends Array<any> ? NonNullable<Type> : NonNullable<Type>[]
type A = ToArray<string> // string[]
type B = ToArray<string[]> // string[]
type C = ToArray<string | number[]> // string[] | number[]
type D = ToArray<string | null> // string[] | never[]
这里唯一的问题是 D
,其中 return 类型是 string[] | never[]
。但是我们可以将整个东西包装成一个 Exclude
来摆脱 never
type ToArray<Type> = Exclude<Type extends Array<any> ? NonNullable<Type> : NonNullable<Type>[], never[] | never>
type A = ToArray<string> // string[]
type B = ToArray<string[]> // string[]
type C = ToArray<string | number[]> // string[] | number[]
type D = ToArray<string | null> // string[]
这个问题对我来说有点难以表达,这可能是我没有找到任何研究的原因。
我正在尝试获得一个最终只有数组的类型。我总是想以“一级”数组结束(如果给出数组的数组,我不关心输出。这在我的情况下不会发生)。所以想要的结果如下:
type T1 = ForceArray<string> // wanted: string[]
type T2 = ForceArray<string[]> // wanted: string[]
我可以用下面的代码来做到这一点:
type ForceArray<T> = NonNullable<T> extends Array<any> ? T : Array<T>
这里使用NonNullable
是因为在实际用例中,ForceArray<T>
用于可选的接口属性。这意味着 T
可以是 undefined
,或者换句话说 T = U | undefined
,其中 U
是实际类型(显示的 U = string
或 U = string[]
示例)。
上面的类型定义很好用,联合类型除外。
type T3 = ForceArray<string | string[]> // shows (string | string[])[] but I want string[]
其他联合类型也应转换为数组,这也不起作用:
type T4 = ForceArray<number | string[]> // wanted: number[] | string[]
type T5 = ForceArray<number[] | string> // wanted: number[] | string[]
在打字稿文档中,他们通过谈论 Distributive conditional types 与此相关。但据我了解文档,我的代码应该可以正常工作。所以很明显我没有正确理解文档。
有没有办法获得预期的行为?
附加信息:
产生输出的实际代码非常简单,这就是我认为应该有解决方案的原因:
const forceArray = (value) => Array.isArray(value) ? value : [value];
此函数在这样的上下文中使用,其中 obj
可以实现任何接口(具有可选属性):
const convertObjectValuesToArray = (obj) => {
const newObj = {};
for (const key of obj) {
newObj[key] = forceArray(obj[key]);
}
return newObj;
}
NonNullable
似乎是这里的问题,因为它禁用了类似于括号 []
的通用类型的可分配性。我们可以删除它并用它代替 returned 类型。
type ToArray<Type> = Type extends Array<any> ? NonNullable<Type> : NonNullable<Type>[]
type A = ToArray<string> // string[]
type B = ToArray<string[]> // string[]
type C = ToArray<string | number[]> // string[] | number[]
type D = ToArray<string | null> // string[] | never[]
这里唯一的问题是 D
,其中 return 类型是 string[] | never[]
。但是我们可以将整个东西包装成一个 Exclude
来摆脱 never
type ToArray<Type> = Exclude<Type extends Array<any> ? NonNullable<Type> : NonNullable<Type>[], never[] | never>
type A = ToArray<string> // string[]
type B = ToArray<string[]> // string[]
type C = ToArray<string | number[]> // string[] | number[]
type D = ToArray<string | null> // string[]