-
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
Strict function types #18654
Strict function types #18654
Conversation
I ran RWC on this and saw changes only in elaborations and order of printing union types. No errors were added or removed, and no types changed. The changes appear to result from better type caching leading to less elaboration. The only interesting result was from the Azure Framework tests, where one incorrectly duplicated error disappeared entirely, maybe because the whole error was already issued just prior. I'll run DefinitelyTyped next and look for diffs. |
@sandersn When running DefinitelyTyped, be sure to run both with and without |
Ah, good point. I forgot Take a look at the test files for recompose, rx-lite and rx.wamp (which depends on rx-lite). Recompose is clearer to see (the rx* errors are obscured by overloads in rx-lite):
Notice that the direction of assignability switches, I think because of contravariance. In total, I noticed changes with
I'll take a look at the results with |
@sandersn With the latest commits we revert to always performing a structural comparison when the relationships indicated by |
@@ -2517,7 +2522,7 @@ namespace ts { | |||
return typeReferenceToTypeNode(<TypeReference>type); | |||
} | |||
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { | |||
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); | |||
const name = type.symbol ? symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier("?"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When does this case occur?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This happens when we have a synthetic type parameter, such as the type parameters we create for tuple types and the markerXXXType
type parameters we use for variance determination.
LGTM but I'd like to see what happens if we run RWC with this forced on |
With the latest commits, both RWC and DT crash much more due to running out of memory. I only get about 2/3 of the RWC tests to succeed, and DT crashes at 'bookshelf'. |
@sandersn Is that with or without |
With --strictFunctionTypes |
@sandersn It is likely because we do more structural comparisons of types. There are enough edge cases (e.g. |
Latest commits address code review feedback. Also, we improve error elaboration for generic types where one or more type parameters are invariant. In some cases generic types that are covariant in regular type checking mode become invariant in |
@ahejlsberg do you have feedback on my latest example? |
@ahejlsberg Even though you mention only 3% of type packages are affected, I think the real negative impact is in much higher considering that major packages like angular, express, hapi, cucumber, selenium-webdriver etc. are affected. Hopefully the undocummented "skipLibCheck" option can rescue some projects. |
@xnnkmd The list above doesn't show it, but in practically all cases (including all of the ones you mention) the errors are in the tests associated with the type package, not in the package itself. So, there should be no need to use |
@ahejlsberg Can you post a list of packages where only the package itself is affected? (not the tests) |
gentlemen, pardon me, but i don't understand your attitude towards these mighty new features... even if we break 97% of all code out there, even then:
|
DefinitelyTyped/DefinitelyTyped#20219 fixes the packages on DefinitelyTyped that break with Packages fixed by DefinitelyTyped/DefinitelyTyped#20219:
I also fixed tests in
Notes:
|
With this PR we introduce a
--strictFunctionTypes
mode in which function type parameter positions are checked contravariantly instead of bivariantly. The stricter checking applies to all function types, except those originating in method or construcor declarations. Methods are excluded specifically to ensure generic classes and interfaces (such asArray<T>
) continue to mostly relate covariantly. The impact of strictly checking methods would be a much bigger breaking change as a large number of generic types would become invariant (even so, we may continue to explore this stricter mode).The
--strictFunctionTypes
switch is part of the--strict
family of switches, meaning that it defaults to on in--strict
mode. This PR is therefore a breaking change only in--strict
mode.Consider the following example in which
Animal
is the supertype ofDog
andCat
:The first assignment is permitted in default type checking mode, but flagged as an error in strict function types mode. Intuitively, the default mode permits the assignment because it is possibly sound, whereas strict function types mode makes it an error because it isn't provably sound. In either mode the third assignment is an error because it is never sound.
Another way to describe the example is that the type
(x: T) => void
is bivariant (i.e. covariant or contravariant) forT
in default type checking mode, but contravariant forT
in strict function types mode.Another example:
In
--strictFunctionTypes
mode the first assignment is still permitted becausecompare
is declared as a method. Effectively,T
is bivariant inComparer<T>
because it is used only in method parameter positions. However, changingcompare
to be a property with a function type causes stricter checking to take effect:The first assignment is now an error. Effectively,
T
is contravariant inComparer<T>
because it is used only in function type parameter positions.By the way, note that whereas some languages (e.g. C# and Scala) require variance annotations (
out
/in
or+
/-
), variance emerges naturally from the actual use of a type parameter within a generic type due to TypeScript's structural type system.We also improve type inference involving contravariant positions in this PR:
Above, all inferences for
T
originate in contravariant positions, and we therefore infer the best common subtype forT
. This contrasts with inferences from covariant positions, where we infer the best common supertype. Note that inferences from covariant positions have precedence over inferences from contravariant positions.Fixes #6102.
Fixes #7472.
Fixes #9514.
Fixes #9765.
Fixes #12498.
Fixes #13248.
Fixes #15579.
Fixes #18337.
Fixes #18466.
Also related are #10717 and #14973.