-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Cache inference for recursive mapped types #20622
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
Changes from 2 commits
fd3a185
bea1f81
1db8f32
e2932fb
808abb6
e664a6e
6e00b0e
786fc33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11217,7 +11217,7 @@ namespace ts { | |
* property is computed by inferring from the source property type to X for the type | ||
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). | ||
*/ | ||
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, mappedTypeStack: string[]): Type { | ||
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, visited: Map<true>): Type { | ||
const properties = getPropertiesOfType(source); | ||
let indexInfo = getIndexInfoOfType(source, IndexKind.String); | ||
if (properties.length === 0 && !indexInfo) { | ||
|
@@ -11250,7 +11250,7 @@ namespace ts { | |
|
||
function inferTargetType(sourceType: Type): Type { | ||
inference.candidates = undefined; | ||
inferTypes(inferences, sourceType, templateType, 0, mappedTypeStack); | ||
inferTypes(inferences, sourceType, templateType, 0, visited); | ||
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType; | ||
} | ||
} | ||
|
@@ -11268,9 +11268,8 @@ namespace ts { | |
return undefined; | ||
} | ||
|
||
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, mappedTypeStack?: string[]) { | ||
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, visited?: Map<true>) { | ||
let symbolStack: Symbol[]; | ||
let visited: Map<boolean>; | ||
inferFromTypes(originalSource, originalTarget); | ||
|
||
function inferFromTypes(source: Type, target: Type) { | ||
|
@@ -11424,7 +11423,7 @@ namespace ts { | |
if (visited && visited.get(key)) { | ||
return; | ||
} | ||
(visited || (visited = createMap<boolean>())).set(key, true); | ||
(visited || (visited = createMap<true>())).set(key, true); | ||
// If we are already processing another target type with the same associated symbol (such as | ||
// an instantiation of the same generic type), we do not explore this target as it would yield | ||
// no further inferences. We exclude the static side of classes from this check since it shares | ||
|
@@ -11485,13 +11484,12 @@ namespace ts { | |
// such that direct inferences to T get priority over inferences to Partial<T>, for example. | ||
const inference = getInferenceInfoForType((<IndexType>constraintType).type); | ||
if (inference && !inference.isFixed) { | ||
const key = (source.symbol ? getSymbolId(source.symbol) + "," : "") + getSymbolId(target.symbol); | ||
if (contains(mappedTypeStack, key)) { | ||
const key = (source.symbol ? getSymbolId(source.symbol) + "s" : "") + getSymbolId(target.symbol); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normally we distinguish different types of cache keys with a leading character, rather than a different separator, see There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. Done. |
||
if (visited && visited.has(key)) { | ||
return; | ||
} | ||
(mappedTypeStack || (mappedTypeStack = [])).push(key); | ||
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, mappedTypeStack); | ||
mappedTypeStack.pop(); | ||
(visited || (visited = createMap<true>())).set(key, true); | ||
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target, visited); | ||
if (inferredType) { | ||
const savePriority = priority; | ||
priority |= InferencePriority.MappedType; | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
// @lib: es6, dom | ||
interface A { a: A } | ||
declare let a: A; | ||
type Deep<T> = { [K in keyof T]: Deep<T[K]> } | ||
declare function foo<T>(deep: Deep<T>): T; | ||
const out = foo(a); | ||
|
||
let xhr: XMLHttpRequest; | ||
const out2 = foo(xhr); |
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.
Is this tested anywhere in a more obvious way? e.g.