类型缩小后类型不可分配给自身

Type is not assignable to itself after type narrowing

给定以下代码

type Test = string | boolean | string[] | boolean[]

function test (param: Test) {
  if (!_.isArray(param)) {
    param = [param]
  }

  return param
}

我收到以下错误

(parameter) param: string | boolean Type 'string | boolean' is not assignable to type 'boolean'. Type 'string' is not assignable to type 'boolean'.ts(2322)

解决方法是

type Test = string | boolean | (string | boolean)[]

但我不明白我的解决方案

换句话说,我有一个类型,它由两种任意类型和(我知道很糟糕)这些类型的数组组成。通过 (!_.isArray) 类型保护我(我知道不好)改变了参数。

在类型保护中,我的类型定义被缩小为 'string | boolean'。 Typescript 告诉我无法为这种类型分配我想要的值。

请大神指点理解错误及解决方法

谢谢

这里的问题是,一旦您确定 param 不是数组,当您获取该类型的值并将其放入数组时,它的类型将被限制为 string | boolean文字 Typescript 将类型推断为 [string | boolean],这与 string[] | boolean[] 不同。如果您改为检查每种可能的类型,然后在单独的分支中使用相同的代码行:

type Test = string | boolean | string[] | boolean[]

function test(param: Test) {
  if (typeof param === 'string') {
    param = [param];
  } else if (typeof param === 'boolean') {
    param = [param];
  }

  return param
}

如果不清楚这些类型之间的区别,一个 ((string | boolean)[]) 可以是一个包含字符串和布尔值混合的数组,而另一个 (string[] | boolean[])是一个只有字符串的数组,或者一个只有布尔值的数组。不幸的是,Typescript 在这种情况下不够聪明,无法意识到数组 [param] 本质上是同质的,因为它只有一个元素。

考虑这个解决方案:

type Test = string | boolean | string[] | boolean[]

const test = (param: Test) => Array.isArray(param) ? param : [param]

你不需要改变任何东西。我相信你甚至不需要在这里使用 lodash

只是我的意见: 对我来说,在条件语句

中没有 ! 否定运算符的情况下,它的可读性更高

更新

感谢 @Paul Wheeler 纠正我。

这里是我的缩小类型解决方案

type Test = string | boolean | string[] | boolean[]

const strOrBool = (val: unknown): val is string | boolean =>
    typeof val === 'string' || typeof val === 'boolean'

function test<T extends string>(param: T): [T]
function test<T extends boolean>(param: T): [T]
function test<T extends string, U extends T[]>(param: [...U]): U
function test<T extends boolean, U extends T[]>(param: [...U]): U
function test<T extends Test>(param: T) {
    return strOrBool(param) ? [param] : param
}

const result = test(true) // [true]
const result2 = test('hello') // ["hello"]
const result3 = test(['hello']) // ["hello"]
const result4 = test([true]) // [true]

Playgound

顺便说一句,typescript 不能很好地处理变异值,它可能会导致错误