As you are aware, one of the call signatures of the Array<T>.filter()
method in the standard TypeScript library will narrow the type of the returned array’s element when you pass in a user-defined type guard function as its parameter:
interface Array<T> {
filter<S extends T>(
callbackfn: (value: T, index: number, array: T[]) => value is S,
thisArg?: any
): S[];
}
The hasName()
function is a user-defined type guard, and that works.
But when you call it with a => a.hasName()
instead, the type of that function is inferred to return just boolean
. That’s because user-defined type guard functions do not propagate and are not inferred for you; see microsoft/TypeScript#10734. A type like x is Y
generally gets widened to boolean
by the compiler as soon as you start doing things with it.
You could tell the compiler that your arrow function callback is also a type guard by using an arrow-function return type annotation, like this:
const listA = [new A('John'), new A(), new A('Mark')].filter(
(a): a is A & { name: string } => a.hasName()
);
console.log(listA[0].name.toUpperCase())
Note that the type predicate is a is A & {name: string}
instead of just a is {name: string}
. That’s because, strictly speaking, a type predicate must narrow the value in question, and {name: string}
is not a subtype of A
(since {name:string}
is missing the hasName()
method). You can apparently get away with this is {name: string}
in a method because polymorphic this
is not checked as strictly in a type predicate (as implemented in microsoft/TypeScript#5906). Anyway, the intersection A & {name: string}
fixes it.
CLICK HERE to find out more related problems solutions.