在typescript数组中缩小类型



我有一个MyArray类型的数组。

type Maybe<T> = T | null;
type MyArray =  Maybe<Materials>[] | Maybe<Seasons>[] | Seasons[] | Materials[] | null | undefined;
type Value = Seasons | Materials;

示例代码:


if (!myArray?.length) {
return;
}
const isValid = myArray.includes(value); // typescript is complaining about value

问题是当我高亮value:

Argument of type Seasons | Materials is not assignable to parameter of type 'never'.

我怎样才能消除这个错误?

这个错误实际上是合法的,存在不匹配,因为:

  • 一方面我们有一个Seasons | Materials(值)
  • 另一方面我们有(数组):
    • 一个不接受该值的Seasons[]盒子,因为它可能是一个Materials
    • 或者一盒不接受该值的Materials[],因为它可能是一个Seasons

我们可以尝试使用一个中间的includes函数来修复它,例如:

type ElementOf<A extends any[], B> = B extends A[number] ? B : never
function includes<A extends any[], B>(list: A, element: ElementOf<A, B>): boolean {
return list.includes(element)
}

这个函数确保:

  • 在语义上,我们要么保留一个Seasons[]的盒子,要么保留一个Materials[]的盒子,但是不是一盒(Seasons | Materials)[]
  • 第一个参数必须使用数组
  • 必须使用的值的类型可能包含在第一个参数
  • 的数组中
const isValid = includes(myArray, value)

你可以在这个操场上看看。


之前答:

我很确定我们可以通过简化MyArray类型的定义来解决这个问题:

type MyArray = Maybe<Array<Maybe<Materials> | Maybe<Seasons>>> | undefined
// or: type MyArray = Array<Maybe<Materials> | Maybe<Seasons>> | null | undefined
function foo(myArray: MyArray, value: Value): void {
// myArray: Maybe<Array<Value | null>> | undefined (or, equivalent -> myArray: Array<Value | null> | null | undefined)
if (!myArray?.length) {
return
}
// myArray: Array<Value | null>
const isValid = myArray.includes(value)
console.log(isValid)
}

myArray的类型是数组的联合类型时:

type MyArray =
| Maybe<Materials>[]
| Maybe<Seasons>[]
| null
| undefined

代替联合类型的数组:

type MyArray = Array<Maybe<Materials> | Maybe<Seasons>> | null | undefined

那么使用includes函数时的类型推断就被打破了,所以我们最终得到的类型是never而不是Maybe<Value>(或Value | null,同样的事情)。

我不知道这个行为是故意的还是一个bug,如果在TypeScript存储库中找到一个提到这个的开放问题,或者创建一个新的问题,可能会很有趣。

(游乐场)

最新更新