Michael Lorton
Michael Lorton

Reputation: 44376

Filtering out one possibility in a union type

The following does not compile:

function f(n: number): (number|null) {
  return n > 0 ? n : null;
}

function g(a: number[]): number[] {
  return a.map(f).filter(n => n);
}

The problem is, the compiler does not understand the semantics of filter(), and does not realize that it will omit all falsey entries in the list. As a result, it fails with "Type '(number | null)[]' is not assignable to type 'number[]'."

The following does work:

function g(n: number[]): number[] {
  return n.map(f).filter(n => n).map(n => n!); 

}

but goes through a meaningless loop. This works too

function g(a: number[]): number[] {
  return a.map(f).filter(n => n) as number[];
}

but a cast like that will obscure a multitude of sins. Any other suggestions?

Upvotes: 2

Views: 1097

Answers (1)

Chad Edrupt
Chad Edrupt

Reputation: 1584

You can extract the filtering method out and use Type Guards to make it a little more precise.

Like so:

function f(n: number): (number | null) {
    return n > 0 ? n : null
}

function hasValue(n: number | null): n is number {
    return !!n
}

function g(a: number[]): number[] {
    return a.map(f).filter(hasValue)
}

Note. !!n will filter out 0 as well so not strictly correct but suitable for highlighting type guards

Upvotes: 4

Related Questions