-
Notifications
You must be signed in to change notification settings - Fork 12.8k
instanceof should not narrow in false branch; or structural assignment to class types should not be allowed #33481
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
Comments
Essentially duplicate of #202 TypeScript is a structural type system. That is because JavaScript is mostly a structural system, but only becomes nominal with It has been a long roadmap item to experiment with nominal typing in TypeScript of which #33038 is one potential solution. There are a couple others that the TypeScript team are actually working on. |
It's different from #202 and #33038 because they're trying to make stuff like Applying structural typing to interfaces and non-classes works fine, in general, but starts leaking (like the example above) when applied to classes and So, either one of the following should have failed (if we didn't care about usability/backwards compat),
But both are allowed at the moment and this introduces the unsoundness. How often do people try to assign an object literal to a |
This is an intentional trade-off. Analysis of real code showed that people either use |
@RyanCavanaugh This was however taken into account in PR #32774, granted that's not merged yet so there are no inconsistencies yet. But I did ask here about that and it was determined that there shouldn't be narrowing in the false case. Should that PR be changed to be consistent with |
There's another reason to not want to narrow in the false branch. //package-foo@1.1.0
export class A {}
export class B {}
//package-foo@1.2.0
export class A {}
export class B {}
//f.ts
//Let's say we're importing from 1.1.0 indirectly, somehow
import {A, B} from "indirectly-uses-package-foo-1.1.0";
function f (aOrB : A|B) {
if (aOrB instanceof A) {
//Correctly narrow to A
console.log("I received A");
} else {
//Incorrectly narrow to B
console.log("I received B");
}
}
//index.ts
//Let's say we're importing from 1.2.0 indirectly, somehow
import {A, B} from "indirectly-uses-package-foo-1.2.0";
import {f} from "./f";
f(new A()); Given the above, the output will be, > I received B However, we passed in The call But, internally, Adding a private property already makes classes nominal (correctly so), why not go all the way? |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
TypeScript Version: 3.5.1
Search Terms:
instanceof, narrow, false branch
Code
Expected behavior:
A|B
should not narrow toB
in thefalse
branch.It is possible for
aOrB
to structurally matchA
but not nominally matchA
.instanceof
only checks for a nominal match; not structural match.Actual behavior:
A|B
is narrowed toB
in thefalse
branch and will cause run-time bugs.Playground Link: Playground
Related Issues:
#32774
I'm sure many people have known of this behaviour for a long time, and I never use classes or
instanceof
much. So, I never bothered to create an issue for it. However, the above PR finally motivated me to bring this up.I tried to look for "instanceof narrow false branch" on the issues page but couldn't find anything related.
I'm sure this will get closed as "Working as Intended" or "Duplicate" but I feel like I needed to get this out of my system, anyway =x
The text was updated successfully, but these errors were encountered: