Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

typescript: "narrowing" a union type to one of its component

Suppose I have this union type:

type Foo = {tag: 'A', name: string} | {tag: 'B', value: number}

I am trying to write a function that gets a tag value ('A' | 'B') and then does some business-specific lookup in a list of Foos and returns a matching element. For the purpose of this question we can think of this very trivial function:

function findFirst(foos: Foo[], tag: 'A' | 'B') {
  for (const foo of foos) {
    if (foo.tag === tag) { 
      return foo 
    }
  }
  return undefined
}

Suppose I now call this function passing ‘A’ as the second argument. It is clear that it either returns undefined or a {tag: 'A', name: string} object. It just cannot return a {tag: 'B', value: number} in this situation.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Still, the TSC does not seem to be able to infer this. When writing an expression such as:

findFirst([{tag: 'A', name: 'X'}], 'A')?.name

The TSC errors with Property 'name' does not exist on type 'Foo'..

I am guessing that I need to help the compiler infer the right by expliticly defining the return type of findFirst() but I am not sure how.

(playground)

>Solution :

You need to add a type parameter to the function to capture the actual type of tag being passed in. You then need to filter the Foo union using the Extract predefined conditional type:

function findFirst<T extends Foo['tag']>(foos: Foo[], tag: T) {
  for (const foo of foos) {
    if (foo.tag === tag) { 
      return foo as Extract<Foo, { tag: T }>
    }
  }
  return undefined
}

Playground Link

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading