-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Local Meaning of Union Types (safe assignments) #17188
Comments
I assume, because tracking all those assignments are overhead for the compiler. While in abstract, it is clear that Having built lots of TypeScript code over the last 2 years, I can't say I have ever run across a pattern where that would be valid. |
@kitsonk Thanks for feedback, maybe I don't understand exactly what do you mean "tracking all those assignments". When compiler meets |
Take for example this: declare function process(c: B | C);
function f(x: B | C) {
if (x instanceof C) {
let c = x;
process(c); // c is a reference, what happens in see cannot be easily tracked via CFA
x = c; // what is `x` here? not sure, so re-widen...
}
} There are all sorts of structural things that can occur at run-time which CFA has to sort of try to treat in the most reasonably type safe way. It is always easier to reset a narrowed type than to do complex tracking of narrowed types, and there will always be trade off of how far it can go.
My opinions are mine. I would assume if you had a pattern that was a realistic and common real world pattern where the re-widening was causing usability issues with TypeScript, the team feel that there is a case. At the moment |
@kitsonk things are getting more complicated ) class A {
x: number;
}
class B extends A {
y: number;
}
class C extends A {
z: number;
}
declare function process(c:C);
function f(x: B | C) {
if (x instanceof C) {
let c = x;
x = c; //hover on x it will be of type B | C
x.z = 1; //hover on x it will be of type C and no error
process(x); //no error
}
} What is that? Now it seems to me that some bug exists or maybe rules have changed. |
Actually going back to your original code: class A {
x: number;
}
class B extends A {
y: number;
}
class C extends A {
z: number;
}
declare function process(c: C);
function f(x: B | C) {
if (x instanceof C) {
let c = x;
x = c; //hover on x it will be of type B | C
process(x); //no error
}
} It was never an error anyways... the left hand assignment quick info was This examples shows that CFA tracks it perfectly fine: class A {
x: number;
}
class B extends A {
y: number;
}
class C extends A {
z: number;
}
declare function process(c: C);
declare const d: B;
function f(x: B | C) {
if (x instanceof C) {
let c = x;
x;
x = c; //hover on x it will be of type B | C
x;
process(x); //no error
x = d;
x; // type = D
}
} |
This statement stopped being true when we added control flow analysis. A complicating factor here is that a variable has two types at any point -- its declared type (for |
@RyanCavanaugh thanks for notes, now I see.
Yes, my question was solved by CFA) |
We're pretty far behind on spec updates and I will admit that I don't expect that to change any time soon. A formal specification of CFA behavior is exceptionally difficult to write in prose; it's cliche but "the code is the spec" in terms of that feature. I expect when CFA does make it into the spec it will be a succinct but nonspecific description like "Conforming implementations may temporarily type an expression or declared property to a subtype of its declared type when a reachability analysis deduces that a type guard or type guard expression is in effect" |
@RyanCavanaugh I understand that the main priority of development of TypeScript is new features rather then routine descriptions of it. And of course I don't expect that CFA requires a formal description in the specification but the notion of flow containers would be 👍 . |
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed. |
TypeScript Version: Playground
Code
Preamble:
A type guard for a variable x has no effect if the statements or expressions it guards contain assignments to x.
If compiler knows that the type of right hand expression is the same as narrowed type of parameter (in my case) why does compiler expand type of x to the initial type? Maybe it is possible to check for this type of assignments?
The text was updated successfully, but these errors were encountered: