Skip to content

Defer inference for homomorphic mapped types #20711

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

Merged
merged 17 commits into from
Jan 6, 2018

Conversation

sandersn
Copy link
Member

Fixes #19951 in a more scalable way by deferring inference on property that result from inference of homomorphic mapped types. It makes lodash's uses of PartialDeep have very little overhead, since those inferences are lower priority and will never be fully instantiated.

The principle drawback to this is that printing an inferred mapped type is non-trivial. I poked at the problem for a while and didn't arrive at a satisfactory solution. In this PR I just use {} for printing the type of all deferred symbols. This is fine for interactive use, OK for our baselines, and not sufficient for packages that want to generate a .d.ts from a file that exports an inferred mapped type. However, I don't think this happens often.

This PR obsoletes #20622.

1. Actually return the cached result!
2. Unnest worker function.
3. Improve all the names.
4. Pre-set the cache to undefined to avoid loops. (Not sure this is
needed, though.)
5. Make the new type internal to avoid baseline changes.
6. Cut off recursion in the printing of recursive deferred mapped types.

Note that (6) required introducing a new stack that is exactly like
mappedTypeStack. I think the cache may actually be needed here, not in
the creation of the deferred type.
Compilation didn't finish before! Now it does.
Just print {} for the type of deferred symbols. This is simple although
it loses fidelity pretty badly. It will not be sufficient for projects
that want to export the result of an inference. I don't think any such
projects exist right now, though.
@weswigham
Copy link
Member

This is fine for interactive use

Do you have any tests for quickinfo for one of these deferred types? If it's the same as the type baselines, I'm not sure it'd be a great experience - minimally it'd be a degradation over our current experience, which is never good.

This allows index signature inference to be inferred as well.
A deferred mapped type with a source that has an index signature now
prints {} as the type of the index signature. This avoids an infinite
recursion.
@sandersn
Copy link
Member Author

sandersn commented Jan 3, 2018

@ahejlsberg I think this is ready for you to look at.

@sandersn
Copy link
Member Author

sandersn commented Jan 4, 2018

@weswigham I added quickinfo and completion tests. I didn't add signature info tests since deferred inferred types do not have signatures, at least until we decide to try to infer those too. Given that limitation, the editing experience is .. OK. Completions are fine but quick info just displays {} for the type.

I'm going to try putting in a depth cutoff to the printing, which will cover the common, non-recursive case of depth=1 better.

This makes them compatible in d.ts and slightly less misleading in
services.
@sandersn
Copy link
Member Author

sandersn commented Jan 4, 2018

@weswigham After playing with a cutoff, and then discussion with @mhegazy, I decided to keep the current solution, but emit any instead of {}, since that will never break d.ts, and is slightly less misleading than {} in interactive use.

function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type {
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type {
const key = source.id + "," + target.id;
if (deferredInferenceCache.has(key)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need this cache?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, turns out some of the tests run out of memory without it.

@@ -3601,6 +3609,12 @@ namespace ts {
finalArrayType?: Type; // Final array type of evolving array type
}

/* @internal */
export interface DeferredMappedType extends ObjectType {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call this ReverseMappedType.

@@ -3494,6 +3501,7 @@ namespace ts {
EvolvingArray = 1 << 8, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
ContainsSpread = 1 << 10, // Object literal contains spread operation
Deferred = 1 << 11, // Object contains a deferred inferred property
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReverseMapped?

@@ -3260,6 +3261,12 @@ namespace ts {
isRestParameter?: boolean;
}

/* @internal */
export interface DeferredTransientSymbol extends TransientSymbol {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReverseMappedSymbol?

inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack);
return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType;
}
function inferDeferredMappedType(sourceType: Type, target: MappedType): Type {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getTypeOfReverseMappedSymbol and pass in the symbol.

Copy link
Member Author

@sandersn sandersn Jan 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called separately when getting the type of the index signature, which doesn't have a symbol. However, I think uniformity is valuable enough here to create a simple getTypeOfReverseMappedSymbol that delegates to the original.

@sandersn sandersn merged commit e78ac47 into master Jan 6, 2018
@sandersn sandersn deleted the defer-inference-for-recursive-mapped-types branch January 6, 2018 00:01
@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants