Consider the following type and sub-types:
interface Animal {
type: string;
}
interface Dog extends Animal {
type: 'dog';
sound: 'bark';
}
interface Cat extends Animal {
type: 'cat';
purpose: 'world_domination';
}
There's also a way to get an Animal sub-type from the value of type:
type Animals = Dog | Cat;
type AnimalFromType<T extends Animals['type']> = Extract<Animals, { type: T }>;
Now let's say I have an array of animals, and I want to create a function that returns a user-defined type guard which can then be used in an Array.filter() call:
function createFilter<T extends 'dog' | 'cat'>(type: T): (x: any) => x is AnimalFromType<T> {
return x => x.type === type;
}
The problem is that it does not seem possible to have functions return type guard functions - the compiler complains with TS2322 that (x: any) => boolean is not assignable to (x: any) => x is AnimalFromType<T> - signature must be a type predicate. For some reason, the compiler does not understand that the returned boolean expression from the lambda matches the x is AnimalFromType<T> part of the outer function's signature.
Why is that? What would stop the compiler from understanding that createFilter('dog') returns a user-defined type guard (which takes an object and returns x is Dog)?
Edit: Full MRE provided by @jcalz here: https://tsplay.dev/wX219m