-
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 type parameters from indexes on those parameters #20126
Conversation
src/compiler/checker.ts
Outdated
@@ -11252,6 +11251,27 @@ namespace ts { | |||
} | |||
return; | |||
} | |||
else if (target.flags & TypeFlags.IndexedAccess) { |
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.
The odd location of this branch within inferTypes
is to work around #20124 - if I placed it below the other IndexedAccess
case (as I'd prefer to do), it would never be executed because of that bug.
src/compiler/checker.ts
Outdated
map.templateType = source; | ||
map.constraintType = (<IndexedAccessType>target).indexType; | ||
map.typeParameter = <TypeParameter>createType(TypeFlags.TypeParameter); | ||
// TODO (weswigham): Ensure the name chosen for the unused "K" does not shadow any other type variables in the given scope, so as to not have a chance of breaking declaration emit |
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.
I think the correct way to do this is to set a flag on the symbol which tells the node builder to use an autogenerated identifier for the name, to just ensure there's no conflicts with anything in the surrounding scopes.
df6c603
to
eaf2abc
Compare
I have greatly simplified the creation of the partial inference types by introducing a type alias that we lookup and instantiate into the Additionally, I have altered the shape of the partial inference type to be much more precise (in exchange it's a bit more complicated). Previously it was, for some Lastly, I've added way more test coverage. They show that this approach can give some excellent results, even under complex higher-order conditions, while also showing that the basic @DanielRosenwasser as this currently is, can you see a way for the vue ecosystem to adopt a pattern involving inferences like this; do you know what such type definitions would look like, approximately (so they can be tested)? |
this does not seem to work for me; type Options = { k: "a", a: number } | { k: "b", b: string };
declare function f<T extends Options>(p: T["k"]): T;
const x = f("a"); // expect it to be `{ k: "a" }` but it is Options |
@mhegazy In your example, |
eaf2abc
to
031a370
Compare
@mhegazy Done - a partial inference result which is not assignable to the constraint now discriminates the constraint based on the fields available in the partial inference, if possible, then creates a spread with the selected constraint and the partial inference. Pretty nifty; good idea. |
031a370
to
49e1961
Compare
@weswigham can you get @ahejlsberg to take a look at this. i do think this is a good addition to make. |
Thanks for your contribution. This PR has not been updated in a while and cannot be automatically merged at the time being. For housekeeping purposes we are closing stale PRs. If you'd still like to continue working on this PR, please leave a message and one of the maintainers can reopen it. |
I wonder why this never made it into the language, or if there was a reported issue it was meant to solve. I occasionally see people trying this sort of inference and I don't know where to point them for a canonical answer as to why it doesn't work this way. |
Uhh... No real reason other than lack of a driver, I guess? As you can see, there's no backing issue, so it never really got prioritized for discussion. |
Looks like this SO question is also running afoul of this, maybe |
This alters the inference info to accumulate a set of partial inferences created by inferences to indexes on a type parameter. These partial inferences are then combined into one real inference when candidates are created. The partial inferences for
S
toT[K]
take the form of a mapped type of{[Key in K]: S}
. I'm unsure if the priority of such an inference needs modification in some way. Creating such a type without any backing parse tree nodes required some slight modification to how a handful of attributes of a mapped type were looked up (namely, adding a cache for anything normally looked up on the declaration).@sandersn and @DanielRosenwasser and I were talking the other day about how inference like this allows you to replace a large list of type parameters with an unordered bag of type parameters, like so:
without this change,
x
is instantiated as the intersections of the base constraints onT["TA"]
andT["TY"]
and so isobject
; but with this change we infer{x: {j: number, i: number}}
forT["TA"]
and{y: number}
forT["TY"]
, creating an aggregate inference forT
of{TA: {x: {j: number, i: number}}, TY: {y: number}}
, making the return type{x: {j: number, i: number}} & {y: number}
.This could be particularly useful in Vue, as then they could introduce a bag for the type parameters passed into their many generic functions. This would enable them to add more type arguments as needed for improved inference or for future features, without breaking existing consumers who are manually specifying a subset of parameters (simply make new arguments in the bag optional) and also allowing that bag to be extended (via interface reopening) by any vue plugins, such as vuex (which then enables them to add overloads to vue-core methods which can carry through the new type information they provide).
I'm sure there's a bunch of refinements to be made, but does anyone have thoughts on adding inference of this kind? It doesn't seem to obviously interact with or overwrite any other inferences we do (certainly no common ones).