Search code examples
typescriptunion-typestypeguards

Type is not assignable to itself after type narrowing


Given the following code

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

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

  return param
}

I recieve the following error

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

The solution is

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

But I don't understand my solution 😅

To put it into words I have a type which consists of two arbitrary types and (bad I know) arrays of those types. Through (!_.isArray) type guarding I (bad I know) mutate the parameter.

Inside the type guard my type definition is narrowed to 'string | boolean'. Typescript is telling me I can't assign the value I want to this type.

Please give me insight into understanding the error and solution.

Thank you


Solution

  • The issue here is that once you establish that param isn't an array, it's type is constrained to string | boolean, when you take a value of that type and put it in an array literal Typescript infers the type to be [string | boolean] which is different than string[] | boolean[]. If you instead checked for each possible type and then had the same line of code in a separate branch that would work:

    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
    }
    

    In case it isn't clear the difference between these types is that one ((string | boolean)[]) can be an array containing a mix of strings and booleans, where as the other (string[] | boolean[]) is either an array of only strings, or an array of only boolean. Unfortunately Typescript isn't smart enough in this case to realize that the array [param] is inherently homogenous because it has only a single element.