-
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
Infer contextual types from generic return types #29478
Conversation
src/compiler/checker.ts
Outdated
if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) { | ||
const returnMapper = (<InferenceContext>getContextualMapper(node)).returnMapper; | ||
if (returnMapper) { | ||
return mapType(contextualType, t => t.flags & TypeFlags.Instantiable ? instantiateType(t, returnMapper) : t); |
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 mapType
call should probably have it's third argument set since we're in contextual typing and preserving string | "x"
's literaliness is desirable.
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.
Also: I'm wondering why bother with the map type/instantiate flag check at all - instantiateType
is only going to instantiate instantiable types anyway.
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.
If you change the mapType
I think you can go ahead and close #29174.
(or ping me and I’ll close it)
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.
@weswigham Agreed, we should make sure we use UnionReduction.None
. The reason we don't just call instantiateType
is that getContextualMapper
isn't a super cheap operation and also that we don't want to instantiate contained object types as it is unnecessary and can be expensive.
@jack-williams I'll fix it so we don't need #29174.
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.
@ahejlsberg Grand, closing the PR. Might be worth adding the example from #29168 to this PR, just to make sure it’s fixed.
src/compiler/checker.ts
Outdated
@@ -22945,7 +22962,9 @@ namespace ts { | |||
context.contextualMapper = contextualMapper; | |||
const checkMode = contextualMapper === identityMapper ? CheckMode.SkipContextSensitive : | |||
contextualMapper ? CheckMode.Inferential : CheckMode.Contextual; | |||
const result = checkExpression(node, checkMode); | |||
const type = checkExpression(node, checkMode); | |||
const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node)) ? |
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.
Why do you need to strip literal freshness here? Minimally that'd deserve a comment, IMO.
Thanks for this PR. 👍 Do we agree it fixes #29168? As a reminder, #29168 is a regression, causing existing valid code to break at compilation. It's already quite difficult for me to understand that regressions are not fixed in the current branch in a project like TypeScript, so I would greatly appreciate that things are managed in a way so it's fixed in TS 3.3. I'm quite worried to see 3.3 planned for January. Time is needed after the PR is merged to check if the issue is solved. |
src/compiler/checker.ts
Outdated
} | ||
} | ||
|
||
// If the given contextual type constains instantiable types and if a mapper representing |
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.
constains -> contains
@ahejlsberg is this practical to port to the 3.3 branch? |
Merge Friday pending any last concerns |
@typescript-bot test this |
Heya @RyanCavanaugh, I've started to run the extended test suite on this PR at 2ea0251. You can monitor the build here. It should now contribute to this PR's status checks. |
@typescript-bot test this again |
Heya @ahejlsberg, I've started to run the extended test suite on this PR at 7c1bb14. You can monitor the build here. It should now contribute to this PR's status checks. |
@RyanCavanaugh There are two new errors in the RWC baselines related to this PR. I have condensed them down to simple repros below. First error: declare function foldLeft<U>(z: U, f: (acc: U, t: boolean) => U): U;
let result: boolean = foldLeft(true, (acc, t) => acc && t); // Error This errors with This issue is basically the opposite of the issue we currently have with Second error: enum State { A, B }
type Foo = { state: State }
declare function bar<T>(f: () => T[]): T[];
let x: Foo[] = bar(() => !!true ? [{ state: State.A }] : [{ state: State.B }]); // Error This errors with I'm not sure there's much we can do in the PR to avoid the errors. Any further tweaks to inference are likely just going to produce more noise. I think it comes down to deciding whether we think they're acceptable breaks. I personally think the fixed scenarios outweigh the two new errors. That said, we may want to wait for 3.4, in which case we should revive the fix for #29168. |
I agree the RWC breaks are acceptable |
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.
LGTM - would be best to add the RWC reductions as testcases too
We should have a |
# Conflicts: # src/compiler/checker.ts
When a generic method has one or more type parameters that appear in both parameter and return types and that method is called in a context where a contextual type exists, we now use inferences from the contextual type to the return type to produce contextual types for the argument expressions. Some examples:
Previously, each of the examples above would error because, lacking a contextual type, the literals were widened to type
string
. With this PR, contextual types for the literals in the argument expressions are properly inferred from the intended return types.Fixes #5487 and #11152 (and numerous duplicate issues).