-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generic types not narrowed with instanceof guard #12085
Comments
Here's another distinction: class A {
value: number;
}
class B {
value: number;
}
let v: A | B;
if (v instanceof A) {
// v is A
} class A<T> {
value: T;
}
class B<T> {
value: T;
}
let v: A<number> | B<number>;
if (v instanceof A) {
// v is A<number> | B<number>
} Aren't these two examples exactly the same? Why would |
This is a #202 issue... Until there is some form of nominal typing in TypeScript, types that are assignable to each other cannot be narrowed with the |
I don't think this is the issue here. Why would the compiler think
I don't see the However, what I see is |
The problem is that the A workaround would be to use a custom type guard: class Left<T> {
// private foo?: void;
constructor(public readonly value: T) {};
}
class Right<T> {
constructor(public readonly value: T) {};
}
function eitherNumberOrString(): Left<number> | Right<string> {
return new Left(0);
}
// e is Left<number> | Right<string>
const e = eitherNumberOrString();
function isLeft<T>(value: any): value is Left<T> {
return value instanceof Left;
}
if (isLeft<number>(e)) {
e.value; // e.value is number
} |
Thanks for the explanation, that bit wasn't obvious to me. Especially since if the type guard succeeds, it narrows the type to the correct generic argument and not class Value<T> {
value: T;
}
class Person {
name: string;
}
let x: Value<string> | Person;
// instanceof assumes Value<any>?
if (x instanceof Value) {
// Narrowed to Value<string>, not Value<any>
x.value;
}` I assume this is expected because the type guard does something like intersecting |
I didn't notice at first your assignment to the generics. I thought they were the same values. When I looked at it again, I realised where my first explanation fell down
It is assignability, class Value { } Which has no methods or properties, TypeScript assumes that is There is one side note in this, which would be a seperate feature request though... the construct of |
I'm not sure this would work, both class Value<T> { value: T }
let x: Value<string> | Value<number>;
if (x instanceof Value<string>) {
// Compiler thinks x is Value<string>
// Runtime will enter this branch for Value<number> as well
} |
TypeScript Version: 2.0.3
Code
Expected behavior:
Left<L> | Right<R>
should be narrowed toLeft<L>
after using ainstanceof Left
type guardActual behavior:
Type is not narrowed. Type is only narrowed if
Left
andRight
are guaranteed to be different types (e.g., by adding aprivate foo?: void
member toLeft
).The text was updated successfully, but these errors were encountered: