You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
typeUnion<A,B>=A|BtypeUnionAny<T>=Union<T,any>typeUnionUnknown<T>=Union<unknown,T>// these should all be the same type but are respectively: any, any, unknown typeUA0=Union<unknown,any>// ^?// anytypeUA1=UnionAny<unknown>// ^?// anytypeUA2=UnionUnknown<any>// ^?// unknown
any "eats" everything.
unknown does too, except for any.
Problem is that we say SomeUninstantiatedTypeVariable | unknown, we eagerly reduce that to unknown.
We would need to defer union reduction in the presence of generics and unknown or any.
That would be very annoying - you wouldn't be able to say that unioning with the top type(s) just reduces to the top type.
[[Editor note]]: probably also have to thread through contexts where you have to know when to reduce the type.
It certainly is an inconsistency, but did it come up in a practical context?
That is unclear.
Conclusion: we admit that it is inconsistent and undesirable, but we are not fully convinced that it needs to change.
Disallowing More Property Access Operations on never-Typed Expressions
From a purely type-theoretic standpoint, there is no issue with accessing a property on never, the issue is arguably more that the code is unreachable.
Could argue that you should say "this whole block is unreachable, so why do you have code?"
That is too heavy-handed - the following doesn't deserve an error, does it?
functionfoo(x: "a"|"b"|"c"): string{switch(x){case"a":
case"b":
case"c":
return"hello";default:
throwDebug.fail("Wrong value "+x);}}
Two common views of never are
"Consuming" a value of type never to enforce an exhaustiveness check.
"Probing" a value of type never to report a helpful error for a violation of the type system or usage from untyped code.
In practice, this change actually breaks a bunch of existing code.
Conclusion: feels like we don't have an appetite
Preserving Type Refinements Within Closures Past Their Last Assignments
When a closure is created after all assignments to some closed-over variable, then you can effectively think of that variable as unchanging.
Effectively safe to preserve the type-assignments so long as the closure isn't hoisted.
Means things like object methods, function expressions, and class expressions can observe prior type narrowing/refinment.
In theory, class declarations could also be made to work here.
We noticed that class declarations were considered "hoisted"...but they're not!
Note that we don't quite narrow in a lexical manner within "compound statements". We use the "end" of the compound statement as the source of the last assignment.
functionf5(condition: boolean){letx: number|undefined;if(condition){x=1;action(()=>x/*number | undefined*/)}else{x=2;action(()=>x/*number | undefined*/)}// <- we consider this to be the position of the last assignment of 'x'action(()=>x/*number*/)}
This approach lends to a fast and efficient check.
It would also be tricky in the case of loops, where the "last" assignment cannot truly be determined.
Variables that are referenced in closures past the last assignment do not necessarily trigger an implicit any warning anymore.
letx;x=42;doSomething(()=>x));// ~// Implicit any error! ❌x="hello!";doSomething(()=>x));// No error! ✅
If we limit to let and parameters, then we don't have any issues with our perf suite - but it does for var on monaco.
We might have other analyses we can apply.
Function declarations in block scopes should maybe have captured variables appropriately narrowed.
functionf(x: string|number){if(typeofx==="number"){functiong(){x// should this be number, but is not in the PR}returng;}}
Intersections and Unions that Depend on Instantiation Order
#56906
any"eats" everything.unknowndoes too, except forany.SomeUninstantiatedTypeVariable | unknown, we eagerly reduce that tounknown.unknownorany.Disallowing More Property Access Operations on
never-Typed Expressions#56780
From a purely type-theoretic standpoint, there is no issue with accessing a property on
never, the issue is arguably more that the code is unreachable.Could argue that you should say "this whole block is unreachable, so why do you have code?"
That is too heavy-handed - the following doesn't deserve an error, does it?
Two common views of
neverareneverto enforce an exhaustiveness check.neverto report a helpful error for a violation of the type system or usage from untyped code.In practice, this change actually breaks a bunch of existing code.
Conclusion: feels like we don't have an appetite
Preserving Type Refinements Within Closures Past Their Last Assignments
#56908
Fixes lots of issues that get duped to Trade-offs in Control Flow Analysis #9998.
When a closure is created after all assignments to some closed-over variable, then you can effectively think of that variable as unchanging.
Effectively safe to preserve the type-assignments so long as the closure isn't hoisted.
Note that we don't quite narrow in a lexical manner within "compound statements". We use the "end" of the compound statement as the source of the last assignment.
Variables that are referenced in closures past the last assignment do not necessarily trigger an implicit any warning anymore.
If we limit to
letand parameters, then we don't have any issues with our perf suite - but it does forvaron monaco.Function declarations in block scopes should maybe have captured variables appropriately narrowed.