Support type predicate return type at `.when()` callback
thawankeane opened this issue · 4 comments
When working with type predicates, the function .when()
will always accept an type (value: TInput) => TOutput
as argument, but it will be very helpful (if possible) to infer the value
as the return type of predicate
argument, let me show some examples:
Imagine that will have the following classes
class Fish {
public swim() {}
}
class Bird {
public fly() {}
}
class Animal {
public speed: number;
public isFish(): this is Fish {
return !!(this as Fish).swim;
}
public isBird(): this is Bird {
return !!(this as Bird).fly;
}
}
Now, you want to use ts-pattern
to exaustive check an Animal
import { match } from "ts-pattern";
const unknownAnimal: Animal = {
// implementation
};
match(unknownAnimal)
.when(
(animal) => animal.isFish(), // right here the `animal` argument is corrected inferred as an `Animal` as expected
(fish) => { // here, the `fish` argument isn't inferred as `Fish`, instead it's also inferred as `Animal`, what would cause inconsistent types
fish.swim() // ts error cause `swim` does not exists on type `Animal`
}
)
// other checks
Describe the solution you'd like
I would like if the second argument (.when
callback) was typed as the return type of the first argument if possible
Maybe I might be missing something, so feel free to correct me if I'm doing something wrong
Describe alternatives you've considered
I tried to explicit type the callback, but typescript doesn't allow to convert our input to the inferred type guard
Example:
.when(
(animal) => animal.isFish(),
(fish: Fish) => { // explicit type `Fish`, ts throw an ts(2345) error
}
)
For clarity: You don't want the handler's input narrowed to the return type of the predicate, you want the predicate to act as a type guard.
The main issue is knowing whether the predicate function even is a type guard. The only sensible solution is to explicitly annotate it.
Then it's a matter of typing .when()
to make use of that, if possible.
For clarity: You don't want the handler's input narrowed to the return type of the predicate, you want the predicate to act as a type guard.
Yeah, you're right
The main issue is knowing whether the predicate function even is a type guard. The only sensible solution is to explicitly annotate it.
As I'm using classes based typings, I ended up using .with
in combination with P.instanceOf
, but in a functional approach explicitly annotate would be a solution, could you please share some examples of how these annotations would be?
Taking your example:
match(unknownAnimal)
.when(
(animal): animal is Fish => animal.isFish(),
(fish) => {
fish.swim()
}
)
But I'm not sure if TS Pattern uses that to narrow the handler input.
But I'm not sure if TS Pattern uses that to narrow the handler input.
Yeah, it does. Thank you @darrylnoakes