From 84241efbd473464b1a46af342f7e2acb83946620 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 25 Jan 2021 13:56:02 -0800 Subject: [PATCH 1/4] Optimize interning of reverse mapped types --- src/compiler/checker.ts | 58 +++++++++++-- src/compiler/types.ts | 1 - .../reference/api/tsserverlibrary.d.ts | 3 +- tests/baselines/reference/api/typescript.d.ts | 3 +- .../reference/genericFunctionInference2.types | 6 +- .../isomorphicMappedTypeInference.js | 6 +- .../isomorphicMappedTypeInference.types | 8 +- .../mappedTypeRecursiveInference.errors.txt | 31 ------- .../reverseMappedTypeDeepDeclarationEmit.js | 82 +++++++++++++++++++ ...verseMappedTypeDeepDeclarationEmit.symbols | 81 ++++++++++++++++++ ...reverseMappedTypeDeepDeclarationEmit.types | 72 ++++++++++++++++ .../reverseMappedTypeDeepDeclarationEmit.ts | 30 +++++++ .../fourslash/reverseMappedTypeQuickInfo.ts | 2 +- 13 files changed, 329 insertions(+), 54 deletions(-) delete mode 100644 tests/baselines/reference/mappedTypeRecursiveInference.errors.txt create mode 100644 tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js create mode 100644 tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.symbols create mode 100644 tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.types create mode 100644 tests/cases/compiler/reverseMappedTypeDeepDeclarationEmit.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5966cdc0e54ea..af1716f022b67 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5058,9 +5058,21 @@ namespace ts { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } + function shouldUsePlaceholderForProperty(propertySymbol: Symbol, context: NodeBuilderContext) { + // Use placeholders for reverse mapped types we've either already descended into, or which + // are nested reverse mappings within a mapping over a non-anonymous type. The later is a restriction mostly just to + // reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`. + // Since anonymous types usually come from expressions, this allows us to preserve the output + // for deep mappings which likely come from expressions, while truncating those parts which + // come from mappings over library functions. + return !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped) && ( + contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol) + || (context.reverseMappedStack?.[0] && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous))); + } + function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) { const propertyIsReverseMapped = !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped); - const propertyType = propertyIsReverseMapped && context.flags & NodeBuilderFlags.InReverseMappedType ? + const propertyType = shouldUsePlaceholderForProperty(propertySymbol, context) ? anyType : getTypeOfSymbol(propertySymbol); const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; @@ -5090,16 +5102,20 @@ namespace ts { } } else { - const savedFlags = context.flags; - context.flags |= propertyIsReverseMapped ? NodeBuilderFlags.InReverseMappedType : 0; let propertyTypeNode: TypeNode; - if (propertyIsReverseMapped && !!(savedFlags & NodeBuilderFlags.InReverseMappedType)) { + if (shouldUsePlaceholderForProperty(propertySymbol, context)) { propertyTypeNode = createElidedInformationPlaceholder(context); } else { + if (propertyIsReverseMapped) { + context.reverseMappedStack ||= []; + context.reverseMappedStack.push(propertySymbol as ReverseMappedSymbol); + } propertyTypeNode = propertyType ? serializeTypeForDeclaration(context, propertyType, propertySymbol, saveEnclosingDeclaration) : factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + if (propertyIsReverseMapped) { + context.reverseMappedStack!.pop(); + } } - context.flags = savedFlags; const modifiers = isReadonlySymbol(propertySymbol) ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined; if (modifiers) { @@ -7608,6 +7624,7 @@ namespace ts { typeParameterNamesByText?: Set; usedSymbolNames?: Set; remappedSymbolNames?: ESMap; + reverseMappedStack?: ReverseMappedSymbol[]; } function isDefaultBindingContext(location: Node) { @@ -10659,6 +10676,14 @@ namespace ts { } } + type ReplaceableIndexedAccessType = IndexedAccessType & { objectType: TypeParameter, indexType: TypeParameter }; + function replaceIndexedAccess(instantiable: Type, type: ReplaceableIndexedAccessType, replacement: Type) { + // map type.indexType to 0 + // map type.objectType to `[TReplacement]` + // thus making the indexed access `[TReplacement][0]` or `TReplacement` + return instantiateType(instantiable, createTypeMapper([type.indexType, type.objectType], [getLiteralType(0), createTupleType([replacement])])); + } + function resolveReverseMappedTypeMembers(type: ReverseMappedType) { const indexInfo = getIndexInfoOfType(type.source, IndexKind.String); const modifiers = getMappedTypeModifiers(type.mappedType); @@ -10672,8 +10697,21 @@ namespace ts { inferredProp.declarations = prop.declarations; inferredProp.nameType = getSymbolLinks(prop).nameType; inferredProp.propertyType = getTypeOfSymbol(prop); - inferredProp.mappedType = type.mappedType; - inferredProp.constraintType = type.constraintType; + if (type.constraintType.type.flags & TypeFlags.IndexedAccess + && (type.constraintType.type as IndexedAccessType).objectType.flags & TypeFlags.TypeParameter + && (type.constraintType.type as IndexedAccessType).indexType.flags & TypeFlags.TypeParameter) { + // A reverse mapping of `{[K in keyof T[K_1]]: T[K_1]}` is the same as that of `{[K in keyof T]: T}`, since all we care about is + // inferring to the "type parameter" (or indexed access) shared by the constraint and template. So, to reduce the number of + // type identities produced, we simplify such indexed access occurences + const newTypeParam = (type.constraintType.type as IndexedAccessType).objectType; + const newMappedType = replaceIndexedAccess(type.mappedType, type.constraintType.type as ReplaceableIndexedAccessType, newTypeParam); + inferredProp.mappedType = newMappedType as MappedType; + inferredProp.constraintType = getIndexType(newTypeParam) as IndexType; + } + else { + inferredProp.mappedType = type.mappedType; + inferredProp.constraintType = type.constraintType; + } members.set(prop.escapedName, inferredProp); } setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined); @@ -20249,7 +20287,11 @@ namespace ts { } function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) { - return inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType); + const links = getSymbolLinks(symbol); + if (!links.type) { + links.type = inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType); + } + return links.type; } function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4352e2f2171e3..fc5cc0dc66371 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4306,7 +4306,6 @@ namespace ts { InObjectTypeLiteral = 1 << 22, InTypeAlias = 1 << 23, // Writing type in type alias declaration InInitialEntityName = 1 << 24, // Set when writing the LHS of an entity name or entity name expression - InReverseMappedType = 1 << 25, } // Ensure the shared flags between this and `NodeBuilderFlags` stay in alignment diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 1598efef3279c..7de1676679fb7 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2271,8 +2271,7 @@ declare namespace ts { IgnoreErrors = 70221824, InObjectTypeLiteral = 4194304, InTypeAlias = 8388608, - InInitialEntityName = 16777216, - InReverseMappedType = 33554432 + InInitialEntityName = 16777216 } export enum TypeFormatFlags { None = 0, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index f8f86d5b88fb2..7072d28e0b3b8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2271,8 +2271,7 @@ declare namespace ts { IgnoreErrors = 70221824, InObjectTypeLiteral = 4194304, InTypeAlias = 8388608, - InInitialEntityName = 16777216, - InReverseMappedType = 33554432 + InInitialEntityName = 16777216 } export enum TypeFormatFlags { None = 0, diff --git a/tests/baselines/reference/genericFunctionInference2.types b/tests/baselines/reference/genericFunctionInference2.types index 75e116d169dd9..85208d6fee42c 100644 --- a/tests/baselines/reference/genericFunctionInference2.types +++ b/tests/baselines/reference/genericFunctionInference2.types @@ -19,7 +19,7 @@ declare const foo: Reducer; const myReducer1: Reducer = combineReducers({ >myReducer1 : Reducer ->combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: any; }; }> +>combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: number; }; }> >combineReducers : (reducers: { [K in keyof S]: Reducer; }) => Reducer >{ combined: combineReducers({ foo }),} : { combined: Reducer<{ foo: number; }>; } @@ -33,8 +33,8 @@ const myReducer1: Reducer = combineReducers({ }); const myReducer2 = combineReducers({ ->myReducer2 : Reducer<{ combined: { foo: any; }; }> ->combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: any; }; }> +>myReducer2 : Reducer<{ combined: { foo: number; }; }> +>combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: number; }; }> >combineReducers : (reducers: { [K in keyof S]: Reducer; }) => Reducer >{ combined: combineReducers({ foo }),} : { combined: Reducer<{ foo: number; }>; } diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.js b/tests/baselines/reference/isomorphicMappedTypeInference.js index aa904e9afa173..778eae4542659 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.js +++ b/tests/baselines/reference/isomorphicMappedTypeInference.js @@ -350,12 +350,14 @@ declare function applySpec(obj: Spec): (...args: any[]) => T; declare var g1: (...args: any[]) => { sum: number; nested: { - mul: any; + mul: string; }; }; declare var g2: (...args: any[]) => { foo: { - bar: any; + bar: { + baz: boolean; + }; }; }; declare const foo: (object: T, partial: Partial) => T; diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.types b/tests/baselines/reference/isomorphicMappedTypeInference.types index 1f1fa0d200a0c..608c5e79df20f 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.types +++ b/tests/baselines/reference/isomorphicMappedTypeInference.types @@ -434,8 +434,8 @@ declare function applySpec(obj: Spec): (...args: any[]) => T; // Infers g1: (...args: any[]) => { sum: number, nested: { mul: string } } var g1 = applySpec({ ->g1 : (...args: any[]) => { sum: number; nested: { mul: any; }; } ->applySpec({ sum: (a: any) => 3, nested: { mul: (b: any) => "n" }}) : (...args: any[]) => { sum: number; nested: { mul: any; }; } +>g1 : (...args: any[]) => { sum: number; nested: { mul: string; }; } +>applySpec({ sum: (a: any) => 3, nested: { mul: (b: any) => "n" }}) : (...args: any[]) => { sum: number; nested: { mul: string; }; } >applySpec : (obj: Spec) => (...args: any[]) => T >{ sum: (a: any) => 3, nested: { mul: (b: any) => "n" }} : { sum: (a: any) => number; nested: { mul: (b: any) => string; }; } @@ -459,8 +459,8 @@ var g1 = applySpec({ // Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); ->g2 : (...args: any[]) => { foo: { bar: any; }; } ->applySpec({ foo: { bar: { baz: (x: any) => true } } }) : (...args: any[]) => { foo: { bar: any; }; } +>g2 : (...args: any[]) => { foo: { bar: { baz: boolean; }; }; } +>applySpec({ foo: { bar: { baz: (x: any) => true } } }) : (...args: any[]) => { foo: { bar: { baz: boolean; }; }; } >applySpec : (obj: Spec) => (...args: any[]) => T >{ foo: { bar: { baz: (x: any) => true } } } : { foo: { bar: { baz: (x: any) => boolean; }; }; } >foo : { bar: { baz: (x: any) => boolean; }; } diff --git a/tests/baselines/reference/mappedTypeRecursiveInference.errors.txt b/tests/baselines/reference/mappedTypeRecursiveInference.errors.txt deleted file mode 100644 index aff22c7e0b368..0000000000000 --- a/tests/baselines/reference/mappedTypeRecursiveInference.errors.txt +++ /dev/null @@ -1,31 +0,0 @@ -tests/cases/compiler/mappedTypeRecursiveInference.ts(19,14): error TS2321: Excessive stack depth comparing types 'XMLHttpRequest' and 'Deep<{ onreadystatechange: unknown; readonly readyState: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly response: unknown; readonly responseText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; responseType: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseURL: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseXML: { readonly URL: any; alinkColor: any; readonly all: any; readonly anchors: any; readonly applets: any; bgColor: any; body: any; readonly characterSet: any; readonly charset: any; readonly compatMode: any; readonly contentType: any; cookie: any; readonly currentScript: any; readonly defaultView: any; designMode: any; dir: any; readonly doctype: any; readonly documentElement: any; readonly documentURI: any; domain: any; readonly embeds: any; fgColor: any; readonly forms: any; readonly fullscreen: any; readonly fullscreenEnabled: any; readonly head: any; readonly hidden: any; readonly images: any; readonly implementation: any; readonly inputEncoding: any; readonly lastModified: any; linkColor: any; readonly links: any; location: any; onfullscreenchange: any; onfullscreenerror: any; onpointerlockchange: any; onpointerlockerror: any; onreadystatechange: any; onvisibilitychange: any; readonly ownerDocument: any; readonly plugins: any; readonly readyState: any; readonly referrer: any; readonly scripts: any; readonly scrollingElement: any; readonly timeline: any; title: any; readonly visibilityState: any; vlinkColor: any; adoptNode: any; captureEvents: any; caretPositionFromPoint: any; caretRangeFromPoint: any; clear: any; close: any; createAttribute: any; createAttributeNS: any; createCDATASection: any; createComment: any; createDocumentFragment: any; createElement: any; createElementNS: any; createEvent: any; createNodeIterator: any; createProcessingInstruction: any; createRange: any; createTextNode: any; createTreeWalker: any; elementFromPoint: any; elementsFromPoint: any; execCommand: any; exitFullscreen: any; exitPointerLock: any; getAnimations: any; getElementById: any; getElementsByClassName: any; getElementsByName: any; getElementsByTagName: any; getElementsByTagNameNS: any; getSelection: any; hasFocus: any; importNode: any; open: any; queryCommandEnabled: any; queryCommandIndeterm: any; queryCommandState: any; queryCommandSupported: any; queryCommandValue: any; releaseEvents: any; write: any; writeln: any; addEventListener: any; removeEventListener: any; readonly baseURI: any; readonly childNodes: any; readonly firstChild: any; readonly isConnected: any; readonly lastChild: any; readonly namespaceURI: any; readonly nextSibling: any; readonly nodeName: any; readonly nodeType: any; nodeValue: any; readonly parentElement: any; readonly parentNode: any; readonly previousSibling: any; textContent: any; appendChild: any; cloneNode: any; compareDocumentPosition: any; contains: any; getRootNode: any; hasChildNodes: any; insertBefore: any; isDefaultNamespace: any; isEqualNode: any; isSameNode: any; lookupNamespaceURI: any; lookupPrefix: any; normalize: any; removeChild: any; replaceChild: any; readonly ATTRIBUTE_NODE: any; readonly CDATA_SECTION_NODE: any; readonly COMMENT_NODE: any; readonly DOCUMENT_FRAGMENT_NODE: any; readonly DOCUMENT_NODE: any; readonly DOCUMENT_POSITION_CONTAINED_BY: any; readonly DOCUMENT_POSITION_CONTAINS: any; readonly DOCUMENT_POSITION_DISCONNECTED: any; readonly DOCUMENT_POSITION_FOLLOWING: any; readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: any; readonly DOCUMENT_POSITION_PRECEDING: any; readonly DOCUMENT_TYPE_NODE: any; readonly ELEMENT_NODE: any; readonly ENTITY_NODE: any; readonly ENTITY_REFERENCE_NODE: any; readonly NOTATION_NODE: any; readonly PROCESSING_INSTRUCTION_NODE: any; readonly TEXT_NODE: any; dispatchEvent: any; oncopy: any; oncut: any; onpaste: any; readonly activeElement: any; readonly fullscreenElement: any; readonly pointerLockElement: any; readonly styleSheets: any; onabort: any; onanimationcancel: any; onanimationend: any; onanimationiteration: any; onanimationstart: any; onauxclick: any; onblur: any; oncancel: any; oncanplay: any; oncanplaythrough: any; onchange: any; onclick: any; onclose: any; oncontextmenu: any; oncuechange: any; ondblclick: any; ondrag: any; ondragend: any; ondragenter: any; ondragexit: any; ondragleave: any; ondragover: any; ondragstart: any; ondrop: any; ondurationchange: any; onemptied: any; onended: any; onerror: any; onfocus: any; ongotpointercapture: any; oninput: any; oninvalid: any; onkeydown: any; onkeypress: any; onkeyup: any; onload: any; onloadeddata: any; onloadedmetadata: any; onloadstart: any; onlostpointercapture: any; onmousedown: any; onmouseenter: any; onmouseleave: any; onmousemove: any; onmouseout: any; onmouseover: any; onmouseup: any; onpause: any; onplay: any; onplaying: any; onpointercancel: any; onpointerdown: any; onpointerenter: any; onpointerleave: any; onpointermove: any; onpointerout: any; onpointerover: any; onpointerup: any; onprogress: any; onratechange: any; onreset: any; onresize: any; onscroll: any; onsecuritypolicyviolation: any; onseeked: any; onseeking: any; onselect: any; onselectionchange: any; onselectstart: any; onstalled: any; onsubmit: any; onsuspend: any; ontimeupdate: any; ontoggle: any; ontouchcancel?: any; ontouchend?: any; ontouchmove?: any; ontouchstart?: any; ontransitioncancel: any; ontransitionend: any; ontransitionrun: any; ontransitionstart: any; onvolumechange: any; onwaiting: any; onwheel: any; readonly childElementCount: any; readonly children: any; readonly firstElementChild: any; readonly lastElementChild: any; append: any; prepend: any; querySelector: any; querySelectorAll: any; createExpression: any; createNSResolver: any; evaluate: any; }; readonly status: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly statusText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; timeout: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly upload: { addEventListener: any; removeEventListener: any; onabort: any; onerror: any; onload: any; onloadend: any; onloadstart: any; onprogress: any; ontimeout: any; dispatchEvent: any; }; withCredentials: { valueOf: any; }; abort: unknown; getAllResponseHeaders: unknown; getResponseHeader: unknown; open: unknown; overrideMimeType: unknown; send: unknown; setRequestHeader: unknown; readonly DONE: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly HEADERS_RECEIVED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly LOADING: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly OPENED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly UNSENT: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; addEventListener: unknown; removeEventListener: unknown; onabort: unknown; onerror: unknown; onload: unknown; onloadend: unknown; onloadstart: unknown; onprogress: unknown; ontimeout: unknown; dispatchEvent: unknown; }>'. -tests/cases/compiler/mappedTypeRecursiveInference.ts(19,18): error TS2321: Excessive stack depth comparing types 'XMLHttpRequest' and 'Deep<{ onreadystatechange: unknown; readonly readyState: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly response: unknown; readonly responseText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; responseType: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseURL: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseXML: { readonly URL: any; alinkColor: any; readonly all: any; readonly anchors: any; readonly applets: any; bgColor: any; body: any; readonly characterSet: any; readonly charset: any; readonly compatMode: any; readonly contentType: any; cookie: any; readonly currentScript: any; readonly defaultView: any; designMode: any; dir: any; readonly doctype: any; readonly documentElement: any; readonly documentURI: any; domain: any; readonly embeds: any; fgColor: any; readonly forms: any; readonly fullscreen: any; readonly fullscreenEnabled: any; readonly head: any; readonly hidden: any; readonly images: any; readonly implementation: any; readonly inputEncoding: any; readonly lastModified: any; linkColor: any; readonly links: any; location: any; onfullscreenchange: any; onfullscreenerror: any; onpointerlockchange: any; onpointerlockerror: any; onreadystatechange: any; onvisibilitychange: any; readonly ownerDocument: any; readonly plugins: any; readonly readyState: any; readonly referrer: any; readonly scripts: any; readonly scrollingElement: any; readonly timeline: any; title: any; readonly visibilityState: any; vlinkColor: any; adoptNode: any; captureEvents: any; caretPositionFromPoint: any; caretRangeFromPoint: any; clear: any; close: any; createAttribute: any; createAttributeNS: any; createCDATASection: any; createComment: any; createDocumentFragment: any; createElement: any; createElementNS: any; createEvent: any; createNodeIterator: any; createProcessingInstruction: any; createRange: any; createTextNode: any; createTreeWalker: any; elementFromPoint: any; elementsFromPoint: any; execCommand: any; exitFullscreen: any; exitPointerLock: any; getAnimations: any; getElementById: any; getElementsByClassName: any; getElementsByName: any; getElementsByTagName: any; getElementsByTagNameNS: any; getSelection: any; hasFocus: any; importNode: any; open: any; queryCommandEnabled: any; queryCommandIndeterm: any; queryCommandState: any; queryCommandSupported: any; queryCommandValue: any; releaseEvents: any; write: any; writeln: any; addEventListener: any; removeEventListener: any; readonly baseURI: any; readonly childNodes: any; readonly firstChild: any; readonly isConnected: any; readonly lastChild: any; readonly namespaceURI: any; readonly nextSibling: any; readonly nodeName: any; readonly nodeType: any; nodeValue: any; readonly parentElement: any; readonly parentNode: any; readonly previousSibling: any; textContent: any; appendChild: any; cloneNode: any; compareDocumentPosition: any; contains: any; getRootNode: any; hasChildNodes: any; insertBefore: any; isDefaultNamespace: any; isEqualNode: any; isSameNode: any; lookupNamespaceURI: any; lookupPrefix: any; normalize: any; removeChild: any; replaceChild: any; readonly ATTRIBUTE_NODE: any; readonly CDATA_SECTION_NODE: any; readonly COMMENT_NODE: any; readonly DOCUMENT_FRAGMENT_NODE: any; readonly DOCUMENT_NODE: any; readonly DOCUMENT_POSITION_CONTAINED_BY: any; readonly DOCUMENT_POSITION_CONTAINS: any; readonly DOCUMENT_POSITION_DISCONNECTED: any; readonly DOCUMENT_POSITION_FOLLOWING: any; readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: any; readonly DOCUMENT_POSITION_PRECEDING: any; readonly DOCUMENT_TYPE_NODE: any; readonly ELEMENT_NODE: any; readonly ENTITY_NODE: any; readonly ENTITY_REFERENCE_NODE: any; readonly NOTATION_NODE: any; readonly PROCESSING_INSTRUCTION_NODE: any; readonly TEXT_NODE: any; dispatchEvent: any; oncopy: any; oncut: any; onpaste: any; readonly activeElement: any; readonly fullscreenElement: any; readonly pointerLockElement: any; readonly styleSheets: any; onabort: any; onanimationcancel: any; onanimationend: any; onanimationiteration: any; onanimationstart: any; onauxclick: any; onblur: any; oncancel: any; oncanplay: any; oncanplaythrough: any; onchange: any; onclick: any; onclose: any; oncontextmenu: any; oncuechange: any; ondblclick: any; ondrag: any; ondragend: any; ondragenter: any; ondragexit: any; ondragleave: any; ondragover: any; ondragstart: any; ondrop: any; ondurationchange: any; onemptied: any; onended: any; onerror: any; onfocus: any; ongotpointercapture: any; oninput: any; oninvalid: any; onkeydown: any; onkeypress: any; onkeyup: any; onload: any; onloadeddata: any; onloadedmetadata: any; onloadstart: any; onlostpointercapture: any; onmousedown: any; onmouseenter: any; onmouseleave: any; onmousemove: any; onmouseout: any; onmouseover: any; onmouseup: any; onpause: any; onplay: any; onplaying: any; onpointercancel: any; onpointerdown: any; onpointerenter: any; onpointerleave: any; onpointermove: any; onpointerout: any; onpointerover: any; onpointerup: any; onprogress: any; onratechange: any; onreset: any; onresize: any; onscroll: any; onsecuritypolicyviolation: any; onseeked: any; onseeking: any; onselect: any; onselectionchange: any; onselectstart: any; onstalled: any; onsubmit: any; onsuspend: any; ontimeupdate: any; ontoggle: any; ontouchcancel?: any; ontouchend?: any; ontouchmove?: any; ontouchstart?: any; ontransitioncancel: any; ontransitionend: any; ontransitionrun: any; ontransitionstart: any; onvolumechange: any; onwaiting: any; onwheel: any; readonly childElementCount: any; readonly children: any; readonly firstElementChild: any; readonly lastElementChild: any; append: any; prepend: any; querySelector: any; querySelectorAll: any; createExpression: any; createNSResolver: any; evaluate: any; }; readonly status: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly statusText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; timeout: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly upload: { addEventListener: any; removeEventListener: any; onabort: any; onerror: any; onload: any; onloadend: any; onloadstart: any; onprogress: any; ontimeout: any; dispatchEvent: any; }; withCredentials: { valueOf: any; }; abort: unknown; getAllResponseHeaders: unknown; getResponseHeader: unknown; open: unknown; overrideMimeType: unknown; send: unknown; setRequestHeader: unknown; readonly DONE: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly HEADERS_RECEIVED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly LOADING: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly OPENED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly UNSENT: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; addEventListener: unknown; removeEventListener: unknown; onabort: unknown; onerror: unknown; onload: unknown; onloadend: unknown; onloadstart: unknown; onprogress: unknown; ontimeout: unknown; dispatchEvent: unknown; }>'. - - -==== tests/cases/compiler/mappedTypeRecursiveInference.ts (2 errors) ==== - interface A { a: A } - declare let a: A; - type Deep = { [K in keyof T]: Deep } - declare function foo(deep: Deep): T; - const out = foo(a); - out.a - out.a.a - out.a.a.a.a.a.a.a - - - interface B { [s: string]: B } - declare let b: B; - const oub = foo(b); - oub.b - oub.b.b - oub.b.a.n.a.n.a - - let xhr: XMLHttpRequest; - const out2 = foo(xhr); - ~~~~~~~~ -!!! error TS2321: Excessive stack depth comparing types 'XMLHttpRequest' and 'Deep<{ onreadystatechange: unknown; readonly readyState: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly response: unknown; readonly responseText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; responseType: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseURL: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseXML: { readonly URL: any; alinkColor: any; readonly all: any; readonly anchors: any; readonly applets: any; bgColor: any; body: any; readonly characterSet: any; readonly charset: any; readonly compatMode: any; readonly contentType: any; cookie: any; readonly currentScript: any; readonly defaultView: any; designMode: any; dir: any; readonly doctype: any; readonly documentElement: any; readonly documentURI: any; domain: any; readonly embeds: any; fgColor: any; readonly forms: any; readonly fullscreen: any; readonly fullscreenEnabled: any; readonly head: any; readonly hidden: any; readonly images: any; readonly implementation: any; readonly inputEncoding: any; readonly lastModified: any; linkColor: any; readonly links: any; location: any; onfullscreenchange: any; onfullscreenerror: any; onpointerlockchange: any; onpointerlockerror: any; onreadystatechange: any; onvisibilitychange: any; readonly ownerDocument: any; readonly plugins: any; readonly readyState: any; readonly referrer: any; readonly scripts: any; readonly scrollingElement: any; readonly timeline: any; title: any; readonly visibilityState: any; vlinkColor: any; adoptNode: any; captureEvents: any; caretPositionFromPoint: any; caretRangeFromPoint: any; clear: any; close: any; createAttribute: any; createAttributeNS: any; createCDATASection: any; createComment: any; createDocumentFragment: any; createElement: any; createElementNS: any; createEvent: any; createNodeIterator: any; createProcessingInstruction: any; createRange: any; createTextNode: any; createTreeWalker: any; elementFromPoint: any; elementsFromPoint: any; execCommand: any; exitFullscreen: any; exitPointerLock: any; getAnimations: any; getElementById: any; getElementsByClassName: any; getElementsByName: any; getElementsByTagName: any; getElementsByTagNameNS: any; getSelection: any; hasFocus: any; importNode: any; open: any; queryCommandEnabled: any; queryCommandIndeterm: any; queryCommandState: any; queryCommandSupported: any; queryCommandValue: any; releaseEvents: any; write: any; writeln: any; addEventListener: any; removeEventListener: any; readonly baseURI: any; readonly childNodes: any; readonly firstChild: any; readonly isConnected: any; readonly lastChild: any; readonly namespaceURI: any; readonly nextSibling: any; readonly nodeName: any; readonly nodeType: any; nodeValue: any; readonly parentElement: any; readonly parentNode: any; readonly previousSibling: any; textContent: any; appendChild: any; cloneNode: any; compareDocumentPosition: any; contains: any; getRootNode: any; hasChildNodes: any; insertBefore: any; isDefaultNamespace: any; isEqualNode: any; isSameNode: any; lookupNamespaceURI: any; lookupPrefix: any; normalize: any; removeChild: any; replaceChild: any; readonly ATTRIBUTE_NODE: any; readonly CDATA_SECTION_NODE: any; readonly COMMENT_NODE: any; readonly DOCUMENT_FRAGMENT_NODE: any; readonly DOCUMENT_NODE: any; readonly DOCUMENT_POSITION_CONTAINED_BY: any; readonly DOCUMENT_POSITION_CONTAINS: any; readonly DOCUMENT_POSITION_DISCONNECTED: any; readonly DOCUMENT_POSITION_FOLLOWING: any; readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: any; readonly DOCUMENT_POSITION_PRECEDING: any; readonly DOCUMENT_TYPE_NODE: any; readonly ELEMENT_NODE: any; readonly ENTITY_NODE: any; readonly ENTITY_REFERENCE_NODE: any; readonly NOTATION_NODE: any; readonly PROCESSING_INSTRUCTION_NODE: any; readonly TEXT_NODE: any; dispatchEvent: any; oncopy: any; oncut: any; onpaste: any; readonly activeElement: any; readonly fullscreenElement: any; readonly pointerLockElement: any; readonly styleSheets: any; onabort: any; onanimationcancel: any; onanimationend: any; onanimationiteration: any; onanimationstart: any; onauxclick: any; onblur: any; oncancel: any; oncanplay: any; oncanplaythrough: any; onchange: any; onclick: any; onclose: any; oncontextmenu: any; oncuechange: any; ondblclick: any; ondrag: any; ondragend: any; ondragenter: any; ondragexit: any; ondragleave: any; ondragover: any; ondragstart: any; ondrop: any; ondurationchange: any; onemptied: any; onended: any; onerror: any; onfocus: any; ongotpointercapture: any; oninput: any; oninvalid: any; onkeydown: any; onkeypress: any; onkeyup: any; onload: any; onloadeddata: any; onloadedmetadata: any; onloadstart: any; onlostpointercapture: any; onmousedown: any; onmouseenter: any; onmouseleave: any; onmousemove: any; onmouseout: any; onmouseover: any; onmouseup: any; onpause: any; onplay: any; onplaying: any; onpointercancel: any; onpointerdown: any; onpointerenter: any; onpointerleave: any; onpointermove: any; onpointerout: any; onpointerover: any; onpointerup: any; onprogress: any; onratechange: any; onreset: any; onresize: any; onscroll: any; onsecuritypolicyviolation: any; onseeked: any; onseeking: any; onselect: any; onselectionchange: any; onselectstart: any; onstalled: any; onsubmit: any; onsuspend: any; ontimeupdate: any; ontoggle: any; ontouchcancel?: any; ontouchend?: any; ontouchmove?: any; ontouchstart?: any; ontransitioncancel: any; ontransitionend: any; ontransitionrun: any; ontransitionstart: any; onvolumechange: any; onwaiting: any; onwheel: any; readonly childElementCount: any; readonly children: any; readonly firstElementChild: any; readonly lastElementChild: any; append: any; prepend: any; querySelector: any; querySelectorAll: any; createExpression: any; createNSResolver: any; evaluate: any; }; readonly status: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly statusText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; timeout: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly upload: { addEventListener: any; removeEventListener: any; onabort: any; onerror: any; onload: any; onloadend: any; onloadstart: any; onprogress: any; ontimeout: any; dispatchEvent: any; }; withCredentials: { valueOf: any; }; abort: unknown; getAllResponseHeaders: unknown; getResponseHeader: unknown; open: unknown; overrideMimeType: unknown; send: unknown; setRequestHeader: unknown; readonly DONE: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly HEADERS_RECEIVED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly LOADING: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly OPENED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly UNSENT: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; addEventListener: unknown; removeEventListener: unknown; onabort: unknown; onerror: unknown; onload: unknown; onloadend: unknown; onloadstart: unknown; onprogress: unknown; ontimeout: unknown; dispatchEvent: unknown; }>'. - ~~~ -!!! error TS2321: Excessive stack depth comparing types 'XMLHttpRequest' and 'Deep<{ onreadystatechange: unknown; readonly readyState: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly response: unknown; readonly responseText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; responseType: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseURL: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; readonly responseXML: { readonly URL: any; alinkColor: any; readonly all: any; readonly anchors: any; readonly applets: any; bgColor: any; body: any; readonly characterSet: any; readonly charset: any; readonly compatMode: any; readonly contentType: any; cookie: any; readonly currentScript: any; readonly defaultView: any; designMode: any; dir: any; readonly doctype: any; readonly documentElement: any; readonly documentURI: any; domain: any; readonly embeds: any; fgColor: any; readonly forms: any; readonly fullscreen: any; readonly fullscreenEnabled: any; readonly head: any; readonly hidden: any; readonly images: any; readonly implementation: any; readonly inputEncoding: any; readonly lastModified: any; linkColor: any; readonly links: any; location: any; onfullscreenchange: any; onfullscreenerror: any; onpointerlockchange: any; onpointerlockerror: any; onreadystatechange: any; onvisibilitychange: any; readonly ownerDocument: any; readonly plugins: any; readonly readyState: any; readonly referrer: any; readonly scripts: any; readonly scrollingElement: any; readonly timeline: any; title: any; readonly visibilityState: any; vlinkColor: any; adoptNode: any; captureEvents: any; caretPositionFromPoint: any; caretRangeFromPoint: any; clear: any; close: any; createAttribute: any; createAttributeNS: any; createCDATASection: any; createComment: any; createDocumentFragment: any; createElement: any; createElementNS: any; createEvent: any; createNodeIterator: any; createProcessingInstruction: any; createRange: any; createTextNode: any; createTreeWalker: any; elementFromPoint: any; elementsFromPoint: any; execCommand: any; exitFullscreen: any; exitPointerLock: any; getAnimations: any; getElementById: any; getElementsByClassName: any; getElementsByName: any; getElementsByTagName: any; getElementsByTagNameNS: any; getSelection: any; hasFocus: any; importNode: any; open: any; queryCommandEnabled: any; queryCommandIndeterm: any; queryCommandState: any; queryCommandSupported: any; queryCommandValue: any; releaseEvents: any; write: any; writeln: any; addEventListener: any; removeEventListener: any; readonly baseURI: any; readonly childNodes: any; readonly firstChild: any; readonly isConnected: any; readonly lastChild: any; readonly namespaceURI: any; readonly nextSibling: any; readonly nodeName: any; readonly nodeType: any; nodeValue: any; readonly parentElement: any; readonly parentNode: any; readonly previousSibling: any; textContent: any; appendChild: any; cloneNode: any; compareDocumentPosition: any; contains: any; getRootNode: any; hasChildNodes: any; insertBefore: any; isDefaultNamespace: any; isEqualNode: any; isSameNode: any; lookupNamespaceURI: any; lookupPrefix: any; normalize: any; removeChild: any; replaceChild: any; readonly ATTRIBUTE_NODE: any; readonly CDATA_SECTION_NODE: any; readonly COMMENT_NODE: any; readonly DOCUMENT_FRAGMENT_NODE: any; readonly DOCUMENT_NODE: any; readonly DOCUMENT_POSITION_CONTAINED_BY: any; readonly DOCUMENT_POSITION_CONTAINS: any; readonly DOCUMENT_POSITION_DISCONNECTED: any; readonly DOCUMENT_POSITION_FOLLOWING: any; readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: any; readonly DOCUMENT_POSITION_PRECEDING: any; readonly DOCUMENT_TYPE_NODE: any; readonly ELEMENT_NODE: any; readonly ENTITY_NODE: any; readonly ENTITY_REFERENCE_NODE: any; readonly NOTATION_NODE: any; readonly PROCESSING_INSTRUCTION_NODE: any; readonly TEXT_NODE: any; dispatchEvent: any; oncopy: any; oncut: any; onpaste: any; readonly activeElement: any; readonly fullscreenElement: any; readonly pointerLockElement: any; readonly styleSheets: any; onabort: any; onanimationcancel: any; onanimationend: any; onanimationiteration: any; onanimationstart: any; onauxclick: any; onblur: any; oncancel: any; oncanplay: any; oncanplaythrough: any; onchange: any; onclick: any; onclose: any; oncontextmenu: any; oncuechange: any; ondblclick: any; ondrag: any; ondragend: any; ondragenter: any; ondragexit: any; ondragleave: any; ondragover: any; ondragstart: any; ondrop: any; ondurationchange: any; onemptied: any; onended: any; onerror: any; onfocus: any; ongotpointercapture: any; oninput: any; oninvalid: any; onkeydown: any; onkeypress: any; onkeyup: any; onload: any; onloadeddata: any; onloadedmetadata: any; onloadstart: any; onlostpointercapture: any; onmousedown: any; onmouseenter: any; onmouseleave: any; onmousemove: any; onmouseout: any; onmouseover: any; onmouseup: any; onpause: any; onplay: any; onplaying: any; onpointercancel: any; onpointerdown: any; onpointerenter: any; onpointerleave: any; onpointermove: any; onpointerout: any; onpointerover: any; onpointerup: any; onprogress: any; onratechange: any; onreset: any; onresize: any; onscroll: any; onsecuritypolicyviolation: any; onseeked: any; onseeking: any; onselect: any; onselectionchange: any; onselectstart: any; onstalled: any; onsubmit: any; onsuspend: any; ontimeupdate: any; ontoggle: any; ontouchcancel?: any; ontouchend?: any; ontouchmove?: any; ontouchstart?: any; ontransitioncancel: any; ontransitionend: any; ontransitionrun: any; ontransitionstart: any; onvolumechange: any; onwaiting: any; onwheel: any; readonly childElementCount: any; readonly children: any; readonly firstElementChild: any; readonly lastElementChild: any; append: any; prepend: any; querySelector: any; querySelectorAll: any; createExpression: any; createNSResolver: any; evaluate: any; }; readonly status: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly statusText: { toString: any; charAt: any; charCodeAt: any; concat: any; indexOf: any; lastIndexOf: any; localeCompare: any; match: any; replace: any; search: any; slice: any; split: any; substring: any; toLowerCase: any; toLocaleLowerCase: any; toUpperCase: any; toLocaleUpperCase: any; trim: any; readonly length: any; substr: any; valueOf: any; codePointAt: any; includes: any; endsWith: any; normalize: any; repeat: any; startsWith: any; anchor: any; big: any; blink: any; bold: any; fixed: any; fontcolor: any; fontsize: any; italics: any; link: any; small: any; strike: any; sub: any; sup: any; [Symbol.iterator]: any; }; timeout: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly upload: { addEventListener: any; removeEventListener: any; onabort: any; onerror: any; onload: any; onloadend: any; onloadstart: any; onprogress: any; ontimeout: any; dispatchEvent: any; }; withCredentials: { valueOf: any; }; abort: unknown; getAllResponseHeaders: unknown; getResponseHeader: unknown; open: unknown; overrideMimeType: unknown; send: unknown; setRequestHeader: unknown; readonly DONE: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly HEADERS_RECEIVED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly LOADING: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly OPENED: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; readonly UNSENT: { toString: any; toFixed: any; toExponential: any; toPrecision: any; valueOf: any; toLocaleString: any; }; addEventListener: unknown; removeEventListener: unknown; onabort: unknown; onerror: unknown; onload: unknown; onloadend: unknown; onloadstart: unknown; onprogress: unknown; ontimeout: unknown; dispatchEvent: unknown; }>'. - out2.responseXML - out2.responseXML.activeElement.className.length - \ No newline at end of file diff --git a/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js new file mode 100644 index 0000000000000..6363966611666 --- /dev/null +++ b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js @@ -0,0 +1,82 @@ +//// [reverseMappedTypeDeepDeclarationEmit.ts] +export type Validator = NativeTypeValidator | ObjectValidator + +export type NativeTypeValidator = (n: any) => T | undefined +export type ObjectValidator = { + [K in keyof O]: Validator +} + +//native validators +export declare const SimpleStringValidator: NativeTypeValidator; + +///object validator function +export declare const ObjValidator: (validatorObj: ObjectValidator) => (o: any) => V; + +export const test = { + Test: { + Test1: { + Test2: SimpleStringValidator + }, + } +} + +export const validatorFunc = ObjValidator(test); +export const outputExample = validatorFunc({ + Test: { + Test1: { + Test2: "hi" + }, + } +}); + + +//// [reverseMappedTypeDeepDeclarationEmit.js] +"use strict"; +exports.__esModule = true; +exports.outputExample = exports.validatorFunc = exports.test = void 0; +exports.test = { + Test: { + Test1: { + Test2: exports.SimpleStringValidator + } + } +}; +exports.validatorFunc = exports.ObjValidator(exports.test); +exports.outputExample = exports.validatorFunc({ + Test: { + Test1: { + Test2: "hi" + } + } +}); + + +//// [reverseMappedTypeDeepDeclarationEmit.d.ts] +export declare type Validator = NativeTypeValidator | ObjectValidator; +export declare type NativeTypeValidator = (n: any) => T | undefined; +export declare type ObjectValidator = { + [K in keyof O]: Validator; +}; +export declare const SimpleStringValidator: NativeTypeValidator; +export declare const ObjValidator: (validatorObj: ObjectValidator) => (o: any) => V; +export declare const test: { + Test: { + Test1: { + Test2: NativeTypeValidator; + }; + }; +}; +export declare const validatorFunc: (o: any) => { + Test: { + Test1: { + Test2: string; + }; + }; +}; +export declare const outputExample: { + Test: { + Test1: { + Test2: string; + }; + }; +}; diff --git a/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.symbols b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.symbols new file mode 100644 index 0000000000000..f7c88c883e6b1 --- /dev/null +++ b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.symbols @@ -0,0 +1,81 @@ +=== tests/cases/compiler/reverseMappedTypeDeepDeclarationEmit.ts === +export type Validator = NativeTypeValidator | ObjectValidator +>Validator : Symbol(Validator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 0)) +>T : Symbol(T, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 22)) +>NativeTypeValidator : Symbol(NativeTypeValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 70)) +>T : Symbol(T, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 22)) +>ObjectValidator : Symbol(ObjectValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 2, 62)) +>T : Symbol(T, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 22)) + +export type NativeTypeValidator = (n: any) => T | undefined +>NativeTypeValidator : Symbol(NativeTypeValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 70)) +>T : Symbol(T, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 2, 32)) +>n : Symbol(n, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 2, 38)) +>T : Symbol(T, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 2, 32)) + +export type ObjectValidator = { +>ObjectValidator : Symbol(ObjectValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 2, 62)) +>O : Symbol(O, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 3, 28)) + + [K in keyof O]: Validator +>K : Symbol(K, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 4, 3)) +>O : Symbol(O, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 3, 28)) +>Validator : Symbol(Validator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 0)) +>O : Symbol(O, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 3, 28)) +>K : Symbol(K, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 4, 3)) +} + +//native validators +export declare const SimpleStringValidator: NativeTypeValidator; +>SimpleStringValidator : Symbol(SimpleStringValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 8, 20)) +>NativeTypeValidator : Symbol(NativeTypeValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 0, 70)) + +///object validator function +export declare const ObjValidator: (validatorObj: ObjectValidator) => (o: any) => V; +>ObjValidator : Symbol(ObjValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 11, 20)) +>V : Symbol(V, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 11, 36)) +>validatorObj : Symbol(validatorObj, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 11, 39)) +>ObjectValidator : Symbol(ObjectValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 2, 62)) +>V : Symbol(V, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 11, 36)) +>o : Symbol(o, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 11, 77)) +>V : Symbol(V, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 11, 36)) + +export const test = { +>test : Symbol(test, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 13, 12)) + + Test: { +>Test : Symbol(Test, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 13, 22)) + + Test1: { +>Test1 : Symbol(Test1, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 14, 9)) + + Test2: SimpleStringValidator +>Test2 : Symbol(Test2, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 15, 12)) +>SimpleStringValidator : Symbol(SimpleStringValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 8, 20)) + + }, + } +} + +export const validatorFunc = ObjValidator(test); +>validatorFunc : Symbol(validatorFunc, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 21, 12)) +>ObjValidator : Symbol(ObjValidator, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 11, 20)) +>test : Symbol(test, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 13, 12)) + +export const outputExample = validatorFunc({ +>outputExample : Symbol(outputExample, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 22, 12)) +>validatorFunc : Symbol(validatorFunc, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 21, 12)) + + Test: { +>Test : Symbol(Test, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 22, 44)) + + Test1: { +>Test1 : Symbol(Test1, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 23, 9)) + + Test2: "hi" +>Test2 : Symbol(Test2, Decl(reverseMappedTypeDeepDeclarationEmit.ts, 24, 12)) + + }, + } +}); + diff --git a/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.types b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.types new file mode 100644 index 0000000000000..48107f9814387 --- /dev/null +++ b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.types @@ -0,0 +1,72 @@ +=== tests/cases/compiler/reverseMappedTypeDeepDeclarationEmit.ts === +export type Validator = NativeTypeValidator | ObjectValidator +>Validator : Validator + +export type NativeTypeValidator = (n: any) => T | undefined +>NativeTypeValidator : NativeTypeValidator +>n : any + +export type ObjectValidator = { +>ObjectValidator : ObjectValidator + + [K in keyof O]: Validator +} + +//native validators +export declare const SimpleStringValidator: NativeTypeValidator; +>SimpleStringValidator : NativeTypeValidator + +///object validator function +export declare const ObjValidator: (validatorObj: ObjectValidator) => (o: any) => V; +>ObjValidator : (validatorObj: ObjectValidator) => (o: any) => V +>validatorObj : ObjectValidator +>o : any + +export const test = { +>test : { Test: { Test1: { Test2: NativeTypeValidator; }; }; } +>{ Test: { Test1: { Test2: SimpleStringValidator }, }} : { Test: { Test1: { Test2: NativeTypeValidator; }; }; } + + Test: { +>Test : { Test1: { Test2: NativeTypeValidator; }; } +>{ Test1: { Test2: SimpleStringValidator }, } : { Test1: { Test2: NativeTypeValidator; }; } + + Test1: { +>Test1 : { Test2: NativeTypeValidator; } +>{ Test2: SimpleStringValidator } : { Test2: NativeTypeValidator; } + + Test2: SimpleStringValidator +>Test2 : NativeTypeValidator +>SimpleStringValidator : NativeTypeValidator + + }, + } +} + +export const validatorFunc = ObjValidator(test); +>validatorFunc : (o: any) => { Test: { Test1: { Test2: string; }; }; } +>ObjValidator(test) : (o: any) => { Test: { Test1: { Test2: string; }; }; } +>ObjValidator : (validatorObj: ObjectValidator) => (o: any) => V +>test : { Test: { Test1: { Test2: NativeTypeValidator; }; }; } + +export const outputExample = validatorFunc({ +>outputExample : { Test: { Test1: { Test2: string; }; }; } +>validatorFunc({ Test: { Test1: { Test2: "hi" }, }}) : { Test: { Test1: { Test2: string; }; }; } +>validatorFunc : (o: any) => { Test: { Test1: { Test2: string; }; }; } +>{ Test: { Test1: { Test2: "hi" }, }} : { Test: { Test1: { Test2: string; }; }; } + + Test: { +>Test : { Test1: { Test2: string; }; } +>{ Test1: { Test2: "hi" }, } : { Test1: { Test2: string; }; } + + Test1: { +>Test1 : { Test2: string; } +>{ Test2: "hi" } : { Test2: string; } + + Test2: "hi" +>Test2 : string +>"hi" : "hi" + + }, + } +}); + diff --git a/tests/cases/compiler/reverseMappedTypeDeepDeclarationEmit.ts b/tests/cases/compiler/reverseMappedTypeDeepDeclarationEmit.ts new file mode 100644 index 0000000000000..e6f8c033e7700 --- /dev/null +++ b/tests/cases/compiler/reverseMappedTypeDeepDeclarationEmit.ts @@ -0,0 +1,30 @@ +// @declaration: true +export type Validator = NativeTypeValidator | ObjectValidator + +export type NativeTypeValidator = (n: any) => T | undefined +export type ObjectValidator = { + [K in keyof O]: Validator +} + +//native validators +export declare const SimpleStringValidator: NativeTypeValidator; + +///object validator function +export declare const ObjValidator: (validatorObj: ObjectValidator) => (o: any) => V; + +export const test = { + Test: { + Test1: { + Test2: SimpleStringValidator + }, + } +} + +export const validatorFunc = ObjValidator(test); +export const outputExample = validatorFunc({ + Test: { + Test1: { + Test2: "hi" + }, + } +}); diff --git a/tests/cases/fourslash/reverseMappedTypeQuickInfo.ts b/tests/cases/fourslash/reverseMappedTypeQuickInfo.ts index c249db25c6969..4f0f454a9fcfe 100644 --- a/tests/cases/fourslash/reverseMappedTypeQuickInfo.ts +++ b/tests/cases/fourslash/reverseMappedTypeQuickInfo.ts @@ -30,7 +30,7 @@ verify.quickInfoAt("1", `type FinalType = { test: { - test_inner: ...; + test_inner: string; }; }`); verify.quickInfoAt("2", `(property) test_inner: string`); From 25616451d39eeea775add44a4fbe312e3980fdfb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 22 Feb 2021 15:04:50 -0800 Subject: [PATCH 2/4] Style feedback --- src/compiler/checker.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 625115a1fc484..490b14966da3d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5114,9 +5114,14 @@ namespace ts { // Since anonymous types usually come from expressions, this allows us to preserve the output // for deep mappings which likely come from expressions, while truncating those parts which // come from mappings over library functions. - return !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped) && ( - contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol) - || (context.reverseMappedStack?.[0] && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous))); + return !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped) + && ( + contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol) + || ( + context.reverseMappedStack?.[0] + && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous) + ) + ); } function addPropertyToElementList(propertySymbol: Symbol, context: NodeBuilderContext, typeElements: TypeElement[]) { From 3c200616499da7aea70fc77b30dfebd6f006d3f2 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 22 Feb 2021 15:32:49 -0800 Subject: [PATCH 3/4] Whitespace --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 490b14966da3d..52cc9b0a4f1ce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5118,9 +5118,9 @@ namespace ts { && ( contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol) || ( - context.reverseMappedStack?.[0] - && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous) - ) + context.reverseMappedStack?.[0] + && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous) + ) ); } From 509639a479dc028d17e45010089a9dbe89658c5a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 4 Mar 2021 15:13:33 -0800 Subject: [PATCH 4/4] Update baseline --- .../reference/reverseMappedTypeDeepDeclarationEmit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js index 6363966611666..aee2773df36c5 100644 --- a/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js +++ b/tests/baselines/reference/reverseMappedTypeDeepDeclarationEmit.js @@ -41,8 +41,8 @@ exports.test = { } } }; -exports.validatorFunc = exports.ObjValidator(exports.test); -exports.outputExample = exports.validatorFunc({ +exports.validatorFunc = (0, exports.ObjValidator)(exports.test); +exports.outputExample = (0, exports.validatorFunc)({ Test: { Test1: { Test2: "hi"