Scottmas
Scottmas

Reputation: 1196

Typescript: Filter array of conditional types AND have proper return type

I want to filter an array of conditional types AND have a proper return type. For example, take the following code:

const foo = {
  type: "foo" as const,
  foo: 123
}

const bar = {
  type: "bar" as const,
  bar: 123
}

const arr = [foo, bar]

const filteredArr = arr.filter(val => val.type === 'foo');

filteredArr.map(val => {
  val.foo //Typescript blows up! Property foo does not exist on type
})

The only solution I've come up with is to cast the filtered array:

type Foo = typeof foo;

const filteredArr = arr.filter(val => val.type === 'foo') as Foo[];

But it seems like there should be a way typescript would be smart enough to infer the array has been filtered down to only Foo[].

Maybe some tricky usage of a user defined type guard?

Upvotes: 5

Views: 2007

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249646

Firstly, the types of foo and bar should preserve the literal type for type so as to have a discriminated union (assuming you probably have type aliases or interfaces for these anyway).

filter will take a custom type guard but can't unfortunately infer a type guard from usage, so the function val => val.type === 'foo' will not narrow the type down.

What we can do though is explicitly annotate the function to let the compiler know we are narrowing the type down in the function:

const foo = {
  type: "foo" as const, // added to preserve ,
  foo: 123
} 

const bar = {
  type: "bar" as const, // added to preserve 
  bar: 123
} 

const arr = [foo, bar]

const filteredArr = arr.filter((val): val is typeof foo => val.type === 'foo')

filteredArr.map(x => x.foo) // ok

Playground Link

Not sure it's a lot better than an assertion, but this is as close as you can get unfortunately.

Upvotes: 5

Related Questions