Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type predicates not working in 'expanded' filter function

I have two classes:

class Dog {
  run() {
    console.log("RUN")
  }
}

class Fish {
  swim() {
    console.log("SWIM")
  }
}

I then make a type union through:

type Pet = Dog | Fish

I then use a type predicate to evaluate what is the superclass of Pet instances:

function isDog(pet: Pet): pet is Dog {
  return (pet as Dog).run !== undefined
}

function isFish(pet: Pet): pet is Fish {
  return (pet as Fish).swim !== undefined
}

I then have an array of Pets:

const zoo: Array<Pet> = [dog, dog, dog, fish, fish, fish]

I would like to filter the array having only Fish instances, I do:

const underwater: Array<Fish> = zoo.filter(isFish)

which works.

Why doesn't this work in the 'expanded' version of the filter function?

const underwater2: Array<Fish> = zoo.filter((p) => isFish(p))
like image 454
Riccardo Perego Avatar asked May 22 '26 07:05

Riccardo Perego


1 Answers

This is the fifth case of open TypeScript issue #38390. Though it might be possible to infer type-predicate return types for simple cases, the main problem seems to be that it would be a breaking change.

Currently, this code type checks:

const pets = zoo.filter(x => isFish(x));
// const pets: Pet[]

pets.push(new Dog()); // Allowed

However, if a type predicate would be inferred for the arrow function, it would fail:

const pets = zoo.filter((p): p is Fish => isFish(p));
// const pets: Fish[]

pets.push(new Dog()); // Type error

If necessary, you can specify the type predicate in the return type of the arrow function (i.e. (p): p is Fish => isFish(p)):

const underwater2: Array<Fish> = zoo.filter((p): p is Fish => isFish(p))
// const underwater2: Fish[]

TypeScript playground

like image 117
Oblosys Avatar answered May 28 '26 01:05

Oblosys



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!