diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5091f5f03063a..d59cf4c5470bf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5921,7 +5921,20 @@ namespace ts { return anyType; } + function getTypeOfSymbolWithDeferredType(symbol: Symbol) { + const links = getSymbolLinks(symbol); + if (!links.type) { + Debug.assertDefined(links.deferralParent); + Debug.assertDefined(links.deferralConstituents); + links.type = links.deferralParent!.flags & TypeFlags.Union ? getUnionType(links.deferralConstituents!) : getIntersectionType(links.deferralConstituents!); + } + return links.type; + } + function getTypeOfSymbol(symbol: Symbol): Type { + if (getCheckFlags(symbol) & CheckFlags.DeferredType) { + return getTypeOfSymbolWithDeferredType(symbol); + } if (getCheckFlags(symbol) & CheckFlags.Instantiated) { return getTypeOfInstantiatedSymbol(symbol); } @@ -8061,7 +8074,15 @@ namespace ts { result.declarations = declarations!; result.nameType = nameType; - result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes); + if (propTypes.length > 2) { + // When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed + result.checkFlags |= CheckFlags.DeferredType; + result.deferralParent = containingType; + result.deferralConstituents = propTypes; + } + else { + result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes); + } return result; } @@ -12313,8 +12334,8 @@ namespace ts { return false; } - function isIgnoredJsxProperty(source: Type, sourceProp: Symbol, targetMemberType: Type | undefined) { - return getObjectFlags(source) & ObjectFlags.JsxAttributes && !(isUnhyphenatedJsxName(sourceProp.escapedName) || targetMemberType); + function isIgnoredJsxProperty(source: Type, sourceProp: Symbol) { + return getObjectFlags(source) & ObjectFlags.JsxAttributes && !isUnhyphenatedJsxName(sourceProp.escapedName); } /** @@ -13495,6 +13516,49 @@ namespace ts { return result || properties; } + function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary { + const targetIsOptional = strictNullChecks && !!(getCheckFlags(targetProp) & CheckFlags.Partial); + const source = getTypeOfSourceProperty(sourceProp); + if (getCheckFlags(targetProp) & CheckFlags.DeferredType && !getSymbolLinks(targetProp).type) { + // Rather than resolving (and normalizing) the type, relate constituent-by-constituent without performing normalization or seconadary passes + const links = getSymbolLinks(targetProp); + Debug.assertDefined(links.deferralParent); + Debug.assertDefined(links.deferralConstituents); + const unionParent = !!(links.deferralParent!.flags & TypeFlags.Union); + let result = unionParent ? Ternary.False : Ternary.True; + const targetTypes = links.deferralConstituents!; + for (const targetType of targetTypes) { + const related = isRelatedTo(source, targetType, /*reportErrors*/ false, /*headMessage*/ undefined, /*isIntersectionConstituent*/ !unionParent); + if (!unionParent) { + if (!related) { + // Can't assign to a target individually - have to fallback to assigning to the _whole_ intersection (which forces normalization) + return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); + } + result &= related; + } + else { + if (related) { + return related; + } + } + } + if (unionParent && !result && targetIsOptional) { + result = isRelatedTo(source, undefinedType); + } + if (unionParent && !result && reportErrors) { + // The easiest way to get the right errors here is to un-defer (which may be costly) + // If it turns out this is too costly too often, we can replicate the error handling logic within + // typeRelatedToSomeType without the discriminatable type branch (as that requires a manifest union + // type on which to hand discriminable properties, which we are expressly trying to avoid here) + return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); + } + return result; + } + else { + return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); + } + } + function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary { const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); @@ -13537,7 +13601,7 @@ namespace ts { return Ternary.False; } // If the target comes from a partial union prop, allow `undefined` in the target type - const related = isRelatedTo(getTypeOfSourceProperty(sourceProp), addOptionality(getTypeOfSymbol(targetProp), !!(getCheckFlags(targetProp) & CheckFlags.Partial)), reportErrors); + const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors); if (!related) { if (reportErrors) { reportError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); @@ -13649,9 +13713,6 @@ namespace ts { if (!(targetProp.flags & SymbolFlags.Prototype)) { const sourceProp = getPropertyOfType(source, targetProp.escapedName); if (sourceProp && sourceProp !== targetProp) { - if (isIgnoredJsxProperty(source, sourceProp, getTypeOfSymbol(targetProp))) { - continue; - } const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors); if (!related) { return Ternary.False; @@ -13797,7 +13858,7 @@ namespace ts { function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary { let result = Ternary.True; for (const prop of getPropertiesOfObjectType(source)) { - if (isIgnoredJsxProperty(source, prop, /*targetMemberType*/ undefined)) { + if (isIgnoredJsxProperty(source, prop)) { continue; } // Skip over symbol-named members diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 03ca196a8608c..3a80655385d36 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3752,6 +3752,8 @@ namespace ts { extendedContainers?: Symbol[]; // Containers (other than the parent) which this symbol is aliased in extendedContainersByFile?: Map; // Containers (other than the parent) which this symbol is aliased in variances?: VarianceFlags[]; // Alias symbol type argument variance cache + deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type + deferralParent?: Type; // Source union/intersection of a deferred type } /* @internal */ @@ -3778,6 +3780,7 @@ namespace ts { ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type OptionalParameter = 1 << 14, // Optional parameter RestParameter = 1 << 15, // Rest parameter + DeferredType = 1 << 16, // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType` Synthetic = SyntheticProperty | SyntheticMethod, Discriminant = HasNonUniformType | HasLiteralType, Partial = ReadPartial | WritePartial diff --git a/tests/baselines/reference/intersectionTypeMembers.js b/tests/baselines/reference/intersectionTypeMembers.js index 59968b007db97..867a6f0fc66c8 100644 --- a/tests/baselines/reference/intersectionTypeMembers.js +++ b/tests/baselines/reference/intersectionTypeMembers.js @@ -43,6 +43,28 @@ const de: D & E = { other: { g: 101 } } } + +// Additional test case with >2 doubly nested members so fix for #31441 is tested w/ excess props +interface F { + nested: { doublyNested: { g: string; } } +} + +interface G { + nested: { doublyNested: { h: string; } } +} + +const defg: D & E & F & G = { + nested: { + doublyNested: { + d: 'yes', + f: 'no', + g: 'ok', + h: 'affirmative' + }, + different: { e: 12 }, + other: { g: 101 } + } +} //// [intersectionTypeMembers.js] @@ -69,3 +91,15 @@ var de = { other: { g: 101 } } }; +var defg = { + nested: { + doublyNested: { + d: 'yes', + f: 'no', + g: 'ok', + h: 'affirmative' + }, + different: { e: 12 }, + other: { g: 101 } + } +}; diff --git a/tests/baselines/reference/intersectionTypeMembers.symbols b/tests/baselines/reference/intersectionTypeMembers.symbols index be47b2960db6f..b4629d1659d9a 100644 --- a/tests/baselines/reference/intersectionTypeMembers.symbols +++ b/tests/baselines/reference/intersectionTypeMembers.symbols @@ -146,3 +146,58 @@ const de: D & E = { } } +// Additional test case with >2 doubly nested members so fix for #31441 is tested w/ excess props +interface F { +>F : Symbol(F, Decl(intersectionTypeMembers.ts, 43, 1)) + + nested: { doublyNested: { g: string; } } +>nested : Symbol(F.nested, Decl(intersectionTypeMembers.ts, 46, 13)) +>doublyNested : Symbol(doublyNested, Decl(intersectionTypeMembers.ts, 47, 13)) +>g : Symbol(g, Decl(intersectionTypeMembers.ts, 47, 29)) +} + +interface G { +>G : Symbol(G, Decl(intersectionTypeMembers.ts, 48, 1)) + + nested: { doublyNested: { h: string; } } +>nested : Symbol(G.nested, Decl(intersectionTypeMembers.ts, 50, 13)) +>doublyNested : Symbol(doublyNested, Decl(intersectionTypeMembers.ts, 51, 13)) +>h : Symbol(h, Decl(intersectionTypeMembers.ts, 51, 29)) +} + +const defg: D & E & F & G = { +>defg : Symbol(defg, Decl(intersectionTypeMembers.ts, 54, 5)) +>D : Symbol(D, Decl(intersectionTypeMembers.ts, 26, 14)) +>E : Symbol(E, Decl(intersectionTypeMembers.ts, 30, 1)) +>F : Symbol(F, Decl(intersectionTypeMembers.ts, 43, 1)) +>G : Symbol(G, Decl(intersectionTypeMembers.ts, 48, 1)) + + nested: { +>nested : Symbol(nested, Decl(intersectionTypeMembers.ts, 54, 29)) + + doublyNested: { +>doublyNested : Symbol(doublyNested, Decl(intersectionTypeMembers.ts, 55, 13)) + + d: 'yes', +>d : Symbol(d, Decl(intersectionTypeMembers.ts, 56, 23)) + + f: 'no', +>f : Symbol(f, Decl(intersectionTypeMembers.ts, 57, 21)) + + g: 'ok', +>g : Symbol(g, Decl(intersectionTypeMembers.ts, 58, 20)) + + h: 'affirmative' +>h : Symbol(h, Decl(intersectionTypeMembers.ts, 59, 20)) + + }, + different: { e: 12 }, +>different : Symbol(different, Decl(intersectionTypeMembers.ts, 61, 10)) +>e : Symbol(e, Decl(intersectionTypeMembers.ts, 62, 20)) + + other: { g: 101 } +>other : Symbol(other, Decl(intersectionTypeMembers.ts, 62, 29)) +>g : Symbol(g, Decl(intersectionTypeMembers.ts, 63, 16)) + } +} + diff --git a/tests/baselines/reference/intersectionTypeMembers.types b/tests/baselines/reference/intersectionTypeMembers.types index eb9d5fa4eb857..817fa044624e8 100644 --- a/tests/baselines/reference/intersectionTypeMembers.types +++ b/tests/baselines/reference/intersectionTypeMembers.types @@ -148,3 +148,61 @@ const de: D & E = { } } +// Additional test case with >2 doubly nested members so fix for #31441 is tested w/ excess props +interface F { + nested: { doublyNested: { g: string; } } +>nested : { doublyNested: { g: string; }; } +>doublyNested : { g: string; } +>g : string +} + +interface G { + nested: { doublyNested: { h: string; } } +>nested : { doublyNested: { h: string; }; } +>doublyNested : { h: string; } +>h : string +} + +const defg: D & E & F & G = { +>defg : D & E & F & G +>{ nested: { doublyNested: { d: 'yes', f: 'no', g: 'ok', h: 'affirmative' }, different: { e: 12 }, other: { g: 101 } }} : { nested: { doublyNested: { d: string; f: string; g: string; h: string; }; different: { e: number; }; other: { g: number; }; }; } + + nested: { +>nested : { doublyNested: { d: string; f: string; g: string; h: string; }; different: { e: number; }; other: { g: number; }; } +>{ doublyNested: { d: 'yes', f: 'no', g: 'ok', h: 'affirmative' }, different: { e: 12 }, other: { g: 101 } } : { doublyNested: { d: string; f: string; g: string; h: string; }; different: { e: number; }; other: { g: number; }; } + + doublyNested: { +>doublyNested : { d: string; f: string; g: string; h: string; } +>{ d: 'yes', f: 'no', g: 'ok', h: 'affirmative' } : { d: string; f: string; g: string; h: string; } + + d: 'yes', +>d : string +>'yes' : "yes" + + f: 'no', +>f : string +>'no' : "no" + + g: 'ok', +>g : string +>'ok' : "ok" + + h: 'affirmative' +>h : string +>'affirmative' : "affirmative" + + }, + different: { e: 12 }, +>different : { e: number; } +>{ e: 12 } : { e: number; } +>e : number +>12 : 12 + + other: { g: 101 } +>other : { g: number; } +>{ g: 101 } : { g: number; } +>g : number +>101 : 101 + } +} + diff --git a/tests/baselines/reference/normalizedIntersectionTooComplex.errors.txt b/tests/baselines/reference/normalizedIntersectionTooComplex.errors.txt index 4e214eff50715..64e7d7f00c510 100644 --- a/tests/baselines/reference/normalizedIntersectionTooComplex.errors.txt +++ b/tests/baselines/reference/normalizedIntersectionTooComplex.errors.txt @@ -1,5 +1,5 @@ -tests/cases/compiler/normalizedIntersectionTooComplex.ts(36,14): error TS2590: Expression produces a union type that is too complex to represent. tests/cases/compiler/normalizedIntersectionTooComplex.ts(36,40): error TS7006: Parameter 'x' implicitly has an 'any' type. +tests/cases/compiler/normalizedIntersectionTooComplex.ts(36,40): error TS2590: Expression produces a union type that is too complex to represent. ==== tests/cases/compiler/normalizedIntersectionTooComplex.ts (2 errors) ==== @@ -39,8 +39,8 @@ tests/cases/compiler/normalizedIntersectionTooComplex.ts(36,40): error TS7006: P declare var all: keyof Big; const ctor = getCtor(all); const comp = ctor({ common: "ok", ref: x => console.log(x) }); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2590: Expression produces a union type that is too complex to represent. ~ !!! error TS7006: Parameter 'x' implicitly has an 'any' type. + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2590: Expression produces a union type that is too complex to represent. \ No newline at end of file diff --git a/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.js b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.js new file mode 100644 index 0000000000000..c85d07ec99b12 --- /dev/null +++ b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.js @@ -0,0 +1,33 @@ +//// [reactTagNameComponentWithPropsNoOOM.tsx] +/// + +import * as React from "react"; +declare const Tag: keyof React.ReactHTML; + +const classes = ""; +const rest: {} = {}; +const children: any[] = []; + +{children} + + +//// [reactTagNameComponentWithPropsNoOOM.js] +"use strict"; +/// +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +exports.__esModule = true; +var React = require("react"); +var classes = ""; +var rest = {}; +var children = []; +React.createElement(Tag, __assign({ className: classes }, rest), children); diff --git a/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.symbols b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.symbols new file mode 100644 index 0000000000000..0b827bf7a0d87 --- /dev/null +++ b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.symbols @@ -0,0 +1,32 @@ +=== tests/cases/compiler/reactTagNameComponentWithPropsNoOOM.tsx === +/// + +import * as React from "react"; +>React : Symbol(React, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 2, 6)) + +declare const Tag: keyof React.ReactHTML; +>Tag : Symbol(Tag, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 3, 13)) +>React : Symbol(React, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 2, 6)) +>ReactHTML : Symbol(React.ReactHTML, Decl(react16.d.ts, 2089, 9)) + +const classes = ""; +>classes : Symbol(classes, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 5, 5)) + +const rest: {} = {}; +>rest : Symbol(rest, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 6, 5)) + +const children: any[] = []; +>children : Symbol(children, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 7, 5)) + + +>Tag : Symbol(Tag, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 3, 13)) +>className : Symbol(className, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 8, 4)) +>classes : Symbol(classes, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 5, 5)) +>rest : Symbol(rest, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 6, 5)) + +{children} +>children : Symbol(children, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 7, 5)) + + +>Tag : Symbol(Tag, Decl(reactTagNameComponentWithPropsNoOOM.tsx, 3, 13)) + diff --git a/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.types b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.types new file mode 100644 index 0000000000000..f31716a55e4c1 --- /dev/null +++ b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM.types @@ -0,0 +1,35 @@ +=== tests/cases/compiler/reactTagNameComponentWithPropsNoOOM.tsx === +/// + +import * as React from "react"; +>React : typeof React + +declare const Tag: keyof React.ReactHTML; +>Tag : "object" | "time" | "link" | "menu" | "dialog" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | "caption" | "cite" | "code" | "col" | "colgroup" | "data" | "datalist" | "dd" | "del" | "details" | "dfn" | "div" | "dl" | "dt" | "em" | "embed" | "fieldset" | "figcaption" | "figure" | "footer" | "form" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "head" | "header" | "hgroup" | "hr" | "html" | "i" | "iframe" | "img" | "input" | "ins" | "kbd" | "keygen" | "label" | "legend" | "li" | "main" | "map" | "mark" | "menuitem" | "meta" | "meter" | "nav" | "noscript" | "ol" | "optgroup" | "option" | "output" | "p" | "param" | "picture" | "pre" | "progress" | "q" | "rp" | "rt" | "ruby" | "s" | "samp" | "script" | "section" | "select" | "small" | "source" | "span" | "strong" | "style" | "sub" | "summary" | "sup" | "table" | "tbody" | "td" | "textarea" | "tfoot" | "th" | "thead" | "title" | "tr" | "track" | "u" | "ul" | "var" | "video" | "wbr" | "webview" +>React : any + +const classes = ""; +>classes : "" +>"" : "" + +const rest: {} = {}; +>rest : {} +>{} : {} + +const children: any[] = []; +>children : any[] +>[] : never[] + + +>{children} : JSX.Element +>Tag : "object" | "time" | "link" | "menu" | "dialog" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | "caption" | "cite" | "code" | "col" | "colgroup" | "data" | "datalist" | "dd" | "del" | "details" | "dfn" | "div" | "dl" | "dt" | "em" | "embed" | "fieldset" | "figcaption" | "figure" | "footer" | "form" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "head" | "header" | "hgroup" | "hr" | "html" | "i" | "iframe" | "img" | "input" | "ins" | "kbd" | "keygen" | "label" | "legend" | "li" | "main" | "map" | "mark" | "menuitem" | "meta" | "meter" | "nav" | "noscript" | "ol" | "optgroup" | "option" | "output" | "p" | "param" | "picture" | "pre" | "progress" | "q" | "rp" | "rt" | "ruby" | "s" | "samp" | "script" | "section" | "select" | "small" | "source" | "span" | "strong" | "style" | "sub" | "summary" | "sup" | "table" | "tbody" | "td" | "textarea" | "tfoot" | "th" | "thead" | "title" | "tr" | "track" | "u" | "ul" | "var" | "video" | "wbr" | "webview" +>className : string +>classes : "" +>rest : {} + +{children} +>children : any[] + + +>Tag : "object" | "time" | "link" | "menu" | "dialog" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | "caption" | "cite" | "code" | "col" | "colgroup" | "data" | "datalist" | "dd" | "del" | "details" | "dfn" | "div" | "dl" | "dt" | "em" | "embed" | "fieldset" | "figcaption" | "figure" | "footer" | "form" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "head" | "header" | "hgroup" | "hr" | "html" | "i" | "iframe" | "img" | "input" | "ins" | "kbd" | "keygen" | "label" | "legend" | "li" | "main" | "map" | "mark" | "menuitem" | "meta" | "meter" | "nav" | "noscript" | "ol" | "optgroup" | "option" | "output" | "p" | "param" | "picture" | "pre" | "progress" | "q" | "rp" | "rt" | "ruby" | "s" | "samp" | "script" | "section" | "select" | "small" | "source" | "span" | "strong" | "style" | "sub" | "summary" | "sup" | "table" | "tbody" | "td" | "textarea" | "tfoot" | "th" | "thead" | "title" | "tr" | "track" | "u" | "ul" | "var" | "video" | "wbr" | "webview" + diff --git a/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.js b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.js new file mode 100644 index 0000000000000..4b6e9d0c54a31 --- /dev/null +++ b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.js @@ -0,0 +1,33 @@ +//// [reactTagNameComponentWithPropsNoOOM2.tsx] +/// + +import * as React from "react"; +declare const Tag: keyof React.ReactHTML; + +const classes = ""; +const rest: React.HTMLAttributes = {}; +const children: any[] = []; + +{children} + + +//// [reactTagNameComponentWithPropsNoOOM2.js] +"use strict"; +/// +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +exports.__esModule = true; +var React = require("react"); +var classes = ""; +var rest = {}; +var children = []; +React.createElement(Tag, __assign({ className: classes }, rest), children); diff --git a/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.symbols b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.symbols new file mode 100644 index 0000000000000..658f9fd5c4393 --- /dev/null +++ b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.symbols @@ -0,0 +1,35 @@ +=== tests/cases/compiler/reactTagNameComponentWithPropsNoOOM2.tsx === +/// + +import * as React from "react"; +>React : Symbol(React, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 2, 6)) + +declare const Tag: keyof React.ReactHTML; +>Tag : Symbol(Tag, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 3, 13)) +>React : Symbol(React, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 2, 6)) +>ReactHTML : Symbol(React.ReactHTML, Decl(react16.d.ts, 2089, 9)) + +const classes = ""; +>classes : Symbol(classes, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 5, 5)) + +const rest: React.HTMLAttributes = {}; +>rest : Symbol(rest, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 6, 5)) +>React : Symbol(React, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 2, 6)) +>HTMLAttributes : Symbol(React.HTMLAttributes, Decl(react16.d.ts, 1048, 9), Decl(react16.d.ts, 1105, 9)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + +const children: any[] = []; +>children : Symbol(children, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 7, 5)) + + +>Tag : Symbol(Tag, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 3, 13)) +>className : Symbol(className, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 8, 4)) +>classes : Symbol(classes, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 5, 5)) +>rest : Symbol(rest, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 6, 5)) + +{children} +>children : Symbol(children, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 7, 5)) + + +>Tag : Symbol(Tag, Decl(reactTagNameComponentWithPropsNoOOM2.tsx, 3, 13)) + diff --git a/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.types b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.types new file mode 100644 index 0000000000000..fac8a844e0323 --- /dev/null +++ b/tests/baselines/reference/reactTagNameComponentWithPropsNoOOM2.types @@ -0,0 +1,36 @@ +=== tests/cases/compiler/reactTagNameComponentWithPropsNoOOM2.tsx === +/// + +import * as React from "react"; +>React : typeof React + +declare const Tag: keyof React.ReactHTML; +>Tag : "object" | "time" | "link" | "menu" | "dialog" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | "caption" | "cite" | "code" | "col" | "colgroup" | "data" | "datalist" | "dd" | "del" | "details" | "dfn" | "div" | "dl" | "dt" | "em" | "embed" | "fieldset" | "figcaption" | "figure" | "footer" | "form" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "head" | "header" | "hgroup" | "hr" | "html" | "i" | "iframe" | "img" | "input" | "ins" | "kbd" | "keygen" | "label" | "legend" | "li" | "main" | "map" | "mark" | "menuitem" | "meta" | "meter" | "nav" | "noscript" | "ol" | "optgroup" | "option" | "output" | "p" | "param" | "picture" | "pre" | "progress" | "q" | "rp" | "rt" | "ruby" | "s" | "samp" | "script" | "section" | "select" | "small" | "source" | "span" | "strong" | "style" | "sub" | "summary" | "sup" | "table" | "tbody" | "td" | "textarea" | "tfoot" | "th" | "thead" | "title" | "tr" | "track" | "u" | "ul" | "var" | "video" | "wbr" | "webview" +>React : any + +const classes = ""; +>classes : "" +>"" : "" + +const rest: React.HTMLAttributes = {}; +>rest : React.HTMLAttributes +>React : any +>{} : {} + +const children: any[] = []; +>children : any[] +>[] : never[] + + +>{children} : JSX.Element +>Tag : "object" | "time" | "link" | "menu" | "dialog" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | "caption" | "cite" | "code" | "col" | "colgroup" | "data" | "datalist" | "dd" | "del" | "details" | "dfn" | "div" | "dl" | "dt" | "em" | "embed" | "fieldset" | "figcaption" | "figure" | "footer" | "form" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "head" | "header" | "hgroup" | "hr" | "html" | "i" | "iframe" | "img" | "input" | "ins" | "kbd" | "keygen" | "label" | "legend" | "li" | "main" | "map" | "mark" | "menuitem" | "meta" | "meter" | "nav" | "noscript" | "ol" | "optgroup" | "option" | "output" | "p" | "param" | "picture" | "pre" | "progress" | "q" | "rp" | "rt" | "ruby" | "s" | "samp" | "script" | "section" | "select" | "small" | "source" | "span" | "strong" | "style" | "sub" | "summary" | "sup" | "table" | "tbody" | "td" | "textarea" | "tfoot" | "th" | "thead" | "title" | "tr" | "track" | "u" | "ul" | "var" | "video" | "wbr" | "webview" +>className : string +>classes : "" +>rest : React.HTMLAttributes + +{children} +>children : any[] + + +>Tag : "object" | "time" | "link" | "menu" | "dialog" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | "caption" | "cite" | "code" | "col" | "colgroup" | "data" | "datalist" | "dd" | "del" | "details" | "dfn" | "div" | "dl" | "dt" | "em" | "embed" | "fieldset" | "figcaption" | "figure" | "footer" | "form" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "head" | "header" | "hgroup" | "hr" | "html" | "i" | "iframe" | "img" | "input" | "ins" | "kbd" | "keygen" | "label" | "legend" | "li" | "main" | "map" | "mark" | "menuitem" | "meta" | "meter" | "nav" | "noscript" | "ol" | "optgroup" | "option" | "output" | "p" | "param" | "picture" | "pre" | "progress" | "q" | "rp" | "rt" | "ruby" | "s" | "samp" | "script" | "section" | "select" | "small" | "source" | "span" | "strong" | "style" | "sub" | "summary" | "sup" | "table" | "tbody" | "td" | "textarea" | "tfoot" | "th" | "thead" | "title" | "tr" | "track" | "u" | "ul" | "var" | "video" | "wbr" | "webview" + diff --git a/tests/cases/compiler/reactTagNameComponentWithPropsNoOOM.tsx b/tests/cases/compiler/reactTagNameComponentWithPropsNoOOM.tsx new file mode 100644 index 0000000000000..e6011974230be --- /dev/null +++ b/tests/cases/compiler/reactTagNameComponentWithPropsNoOOM.tsx @@ -0,0 +1,13 @@ +// @jsx: react +// @strict: true +/// + +import * as React from "react"; +declare const Tag: keyof React.ReactHTML; + +const classes = ""; +const rest: {} = {}; +const children: any[] = []; + +{children} + \ No newline at end of file diff --git a/tests/cases/compiler/reactTagNameComponentWithPropsNoOOM2.tsx b/tests/cases/compiler/reactTagNameComponentWithPropsNoOOM2.tsx new file mode 100644 index 0000000000000..64d46b5dd9512 --- /dev/null +++ b/tests/cases/compiler/reactTagNameComponentWithPropsNoOOM2.tsx @@ -0,0 +1,13 @@ +// @jsx: react +// @strict: true +/// + +import * as React from "react"; +declare const Tag: keyof React.ReactHTML; + +const classes = ""; +const rest: React.HTMLAttributes = {}; +const children: any[] = []; + +{children} + \ No newline at end of file diff --git a/tests/cases/conformance/types/intersection/intersectionTypeMembers.ts b/tests/cases/conformance/types/intersection/intersectionTypeMembers.ts index a350c657f6149..bfb257b8ac2ee 100644 --- a/tests/cases/conformance/types/intersection/intersectionTypeMembers.ts +++ b/tests/cases/conformance/types/intersection/intersectionTypeMembers.ts @@ -42,3 +42,25 @@ const de: D & E = { other: { g: 101 } } } + +// Additional test case with >2 doubly nested members so fix for #31441 is tested w/ excess props +interface F { + nested: { doublyNested: { g: string; } } +} + +interface G { + nested: { doublyNested: { h: string; } } +} + +const defg: D & E & F & G = { + nested: { + doublyNested: { + d: 'yes', + f: 'no', + g: 'ok', + h: 'affirmative' + }, + different: { e: 12 }, + other: { g: 101 } + } +}