Search code examples
typescripttypesnarrowingtypeguards

Type is not narrowed from function using type guards


I have a function that takes parameters of similar structure and inside the function uses type guards to figure out the type of parameter passed in.

type Ex1 = {
  contents: 'contents',
  param1: 'yes'
}

type Ex2 = {
  contents: 'contents',
  param2: true
}

type Ex = Ex1 | Ex2;

export const exampleFunc = (example: Ex): Ex => {
  const isEx1 = (unknownExample: Ex): unknownExample is Ex1 => {
    return (unknownExample as Ex1).param1 === undefined ? false : true;
  }

  const execute = (): Ex => {
    if (isEx1(example)) {
      return example;
    }

    return example;
  }

  return execute()
}

In my actual application, a complex object of a different type is returned, but in this example, i simply return the same object.

When I execute the function

const ex1: Ex1 = {
  contents: 'contents',
  param1: 'yes'
}
const test = exampleFunc(ex1);

In my IDE, I can see that TS correctly understands that example in the following block

if (isEx1(example)) {
  return example;
}

is of the correct type

(parameter) example: Ex1

You can see that the object assigned to example is returned from the function and in this case assigned to test.

However, despite understanding the correct type in the return blocks, Typscript doesn't then assign test as type Ex1 and instead assigns it as the more broad type Ex.

const test = exampleFunc(ex1);

which means that the following throws an error

test.param1

Property 'param1' does not exist on type 'Ex'.

Are type guards only useful in the current execution block or am I misunderstanding their use?


Solution

  • Narrowing only applies to the local scope in which it occurs. Since your exampleFunc declares that it returns an Ex, the type system will treat it as an Ex regardless of what happens inside the function.