I'm trying to make a user type guard defined inside a class instance method work, I'm using latest typescript 4.0.5, and can't find a way to fix it. I'm getting an error (commented below). Here's the typescript playground .
// Example with class method user type guard
class A{
constructor(public name?: string){}
public hasName(): this is {name: string}{
return name !== undefined;
}
}
const listA = [new A('John'), new A(), new A('Mark')].filter(a => a.hasName())
console.log(listA[0].name.toUpperCase()) // Error: Object is possibly undefined
// Example with function user type guard
class B{
constructor(public name?: string){}
}
const hasName = (b: B): b is {name: string} => name !== undefined;
const listB = [new B('John'), new B(), new B('Mark')].filter(hasName);
console.log(listB[0].name.toUpperCase()) // No error, type guard makes Typescript know it can not be undefined
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.