diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 072cc99f2a078..93772d05b2721 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -808,7 +808,13 @@ export function createSortedArray(): SortedArray { } /** @internal */ -export function insertSorted(array: SortedArray, insert: T, compare: Comparer, allowDuplicates?: boolean): boolean { +export function insertSorted( + array: SortedArray, + insert: T, + compare: Comparer, + equalityComparer?: EqualityComparer, + allowDuplicates?: boolean, +): boolean { if (array.length === 0) { array.push(insert); return true; @@ -816,6 +822,16 @@ export function insertSorted(array: SortedArray, insert: T, compare: Compa const insertIndex = binarySearch(array, insert, identity, compare); if (insertIndex < 0) { + if (equalityComparer && !allowDuplicates) { + const idx = ~insertIndex; + if (idx > 0 && equalityComparer(insert, array[idx - 1])) { + return false; + } + if (idx < array.length && equalityComparer(insert, array[idx])) { + array.splice(idx, 1, insert); + return true; + } + } array.splice(~insertIndex, 0, insert); return true; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index aaabffebb2159..867a139c5c8c6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5864,6 +5864,9 @@ export function createDiagnosticCollection(): DiagnosticCollection { if (result >= 0) { return diagnostics[result]; } + if (~result > 0 && diagnosticsEqualityComparer(diagnostic, diagnostics[~result - 1])) { + return diagnostics[~result - 1]; + } return undefined; } @@ -5887,7 +5890,7 @@ export function createDiagnosticCollection(): DiagnosticCollection { diagnostics = nonFileDiagnostics; } - insertSorted(diagnostics, diagnostic, compareDiagnosticsSkipRelatedInformation); + insertSorted(diagnostics, diagnostic, compareDiagnosticsSkipRelatedInformation, diagnosticsEqualityComparer); } function getGlobalDiagnostics(): Diagnostic[] { @@ -8545,12 +8548,14 @@ export function compareDiagnosticsSkipRelatedInformation(d1: Diagnostic, d2: Dia Comparison.EqualTo; } +// A diagnostic with more elaboration should be considered *less than* a diagnostic +// with less elaboration that is otherwise similar. function compareRelatedInformation(d1: Diagnostic, d2: Diagnostic): Comparison { if (!d1.relatedInformation && !d2.relatedInformation) { return Comparison.EqualTo; } if (d1.relatedInformation && d2.relatedInformation) { - return compareValues(d1.relatedInformation.length, d2.relatedInformation.length) || forEach(d1.relatedInformation, (d1i, index) => { + return compareValues(d2.relatedInformation.length, d1.relatedInformation.length) || forEach(d1.relatedInformation, (d1i, index) => { const d2i = d2.relatedInformation![index]; return compareDiagnostics(d1i, d2i); // EqualTo is 0, so falsy, and will cause the next item to be compared }) || Comparison.EqualTo; @@ -8558,45 +8563,115 @@ function compareRelatedInformation(d1: Diagnostic, d2: Diagnostic): Comparison { return d1.relatedInformation ? Comparison.LessThan : Comparison.GreaterThan; } -function compareMessageText(t1: string | DiagnosticMessageChain, t2: string | DiagnosticMessageChain): Comparison { +// An diagnostic message with more elaboration should be considered *less than* a diagnostic message +// with less elaboration that is otherwise similar. +function compareMessageText( + t1: string | Pick, + t2: string | Pick, +): Comparison { if (typeof t1 === "string" && typeof t2 === "string") { return compareStringsCaseSensitive(t1, t2); } - else if (typeof t1 === "string") { - return Comparison.LessThan; + + if (typeof t1 === "string") { + t1 = { messageText: t1 }; } - else if (typeof t2 === "string") { - return Comparison.GreaterThan; + if (typeof t2 === "string") { + t2 = { messageText: t2 }; } - let res = compareStringsCaseSensitive(t1.messageText, t2.messageText); + const res = compareStringsCaseSensitive(t1.messageText, t2.messageText); if (res) { return res; } - if (!t1.next && !t2.next) { + + return compareMessageChain(t1.next, t2.next); +} + +// First compare by size of the message chain, +// then compare by content of the message chain. +function compareMessageChain( + c1: DiagnosticMessageChain[] | undefined, + c2: DiagnosticMessageChain[] | undefined, +): Comparison { + if (c1 === undefined && c2 === undefined) { return Comparison.EqualTo; } - if (!t1.next) { + if (c1 === undefined) { + return Comparison.GreaterThan; + } + if (c2 === undefined) { return Comparison.LessThan; } - if (!t2.next) { + + return compareMessageChainSize(c1, c2) || compareMessageChainContent(c1, c2); +} + +function compareMessageChainSize( + c1: DiagnosticMessageChain[] | undefined, + c2: DiagnosticMessageChain[] | undefined, +): Comparison { + if (c1 === undefined && c2 === undefined) { + return Comparison.EqualTo; + } + if (c1 === undefined) { return Comparison.GreaterThan; } - const len = Math.min(t1.next.length, t2.next.length); - for (let i = 0; i < len; i++) { - res = compareMessageText(t1.next[i], t2.next[i]); + if (c2 === undefined) { + return Comparison.LessThan; + } + + let res = compareValues(c2.length, c1.length); + if (res) { + return res; + } + + for (let i = 0; i < c2.length; i++) { + res = compareMessageChainSize(c1[i].next, c2[i].next); if (res) { return res; } } - if (t1.next.length < t2.next.length) { - return Comparison.LessThan; - } - else if (t1.next.length > t2.next.length) { - return Comparison.GreaterThan; + + return Comparison.EqualTo; +} + +// Assumes the two chains have the same shape. +function compareMessageChainContent( + c1: DiagnosticMessageChain[], + c2: DiagnosticMessageChain[], +): Comparison { + let res; + for (let i = 0; i < c2.length; i++) { + res = compareStringsCaseSensitive(c1[i].messageText, c2[i].messageText); + if (res) { + return res; + } + if (c1[i].next === undefined) { + continue; + } + res = compareMessageChainContent(c1[i].next!, c2[i].next!); + if (res) { + return res; + } } return Comparison.EqualTo; } +/** @internal */ +export function diagnosticsEqualityComparer(d1: Diagnostic, d2: Diagnostic): boolean { + return compareStringsCaseSensitive(getDiagnosticFilePath(d1), getDiagnosticFilePath(d2)) === Comparison.EqualTo && + compareValues(d1.start, d2.start) === Comparison.EqualTo && + compareValues(d1.length, d2.length) === Comparison.EqualTo && + compareValues(d1.code, d2.code) === Comparison.EqualTo && + messageTextEqualityComparer(d1.messageText, d2.messageText); +} + +function messageTextEqualityComparer(m1: string | DiagnosticMessageChain, m2: string | DiagnosticMessageChain): boolean { + const t1 = typeof m1 === "string" ? m1 : m1.messageText; + const t2 = typeof m2 === "string" ? m2 : m2.messageText; + return compareStringsCaseSensitive(t1, t2) === Comparison.EqualTo; +} + /** @internal */ export function getLanguageVariant(scriptKind: ScriptKind) { // .tsx and .jsx files are treated as jsx language variant. diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 60e19b0e4708a..35cabc9011aae 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -49,6 +49,7 @@ import { Decorator, Diagnostic, Diagnostics, + diagnosticsEqualityComparer, ElementAccessChain, ElementAccessExpression, emptyArray, @@ -304,7 +305,7 @@ export function isExternalModuleNameRelative(moduleName: string): boolean { } export function sortAndDeduplicateDiagnostics(diagnostics: readonly T[]): SortedReadonlyArray { - return sortAndDeduplicate(diagnostics, compareDiagnostics); + return sortAndDeduplicate(diagnostics, compareDiagnostics, diagnosticsEqualityComparer); } export function getDefaultLibFileName(options: CompilerOptions): string { diff --git a/src/services/completions.ts b/src/services/completions.ts index 5c70518263c31..386e53f2d8e19 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1340,7 +1340,7 @@ function completionInfoFromData( !uniqueNames.has(keywordEntry.name) ) { uniqueNames.add(keywordEntry.name); - insertSorted(entries, keywordEntry, compareCompletionEntries, /*allowDuplicates*/ true); + insertSorted(entries, keywordEntry, compareCompletionEntries, /*equalityComparer*/ undefined, /*allowDuplicates*/ true); } } } @@ -1348,14 +1348,14 @@ function completionInfoFromData( for (const keywordEntry of getContextualKeywords(contextToken, position)) { if (!uniqueNames.has(keywordEntry.name)) { uniqueNames.add(keywordEntry.name); - insertSorted(entries, keywordEntry, compareCompletionEntries, /*allowDuplicates*/ true); + insertSorted(entries, keywordEntry, compareCompletionEntries, /*equalityComparer*/ undefined, /*allowDuplicates*/ true); } } for (const literal of literals) { const literalEntry = createCompletionEntryForLiteral(sourceFile, preferences, literal); uniqueNames.add(literalEntry.name); - insertSorted(entries, literalEntry, compareCompletionEntries, /*allowDuplicates*/ true); + insertSorted(entries, literalEntry, compareCompletionEntries, /*equalityComparer*/ undefined, /*allowDuplicates*/ true); } if (!isChecked) { @@ -2630,7 +2630,7 @@ export function getCompletionEntriesFromSymbols( /** True for locals; false for globals, module exports from other files, `this.` completions. */ const shouldShadowLaterSymbols = (!origin || originIsTypeOnlyAlias(origin)) && !(symbol.parent === undefined && !some(symbol.declarations, d => d.getSourceFile() === location.getSourceFile())); uniques.set(name, shouldShadowLaterSymbols); - insertSorted(entries, entry, compareCompletionEntries, /*allowDuplicates*/ true); + insertSorted(entries, entry, compareCompletionEntries, /*equalityComparer*/ undefined, /*allowDuplicates*/ true); } log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (timestamp() - start)); diff --git a/src/testRunner/tests.ts b/src/testRunner/tests.ts index fffd8d92a6d05..75a0ca1d32c70 100644 --- a/src/testRunner/tests.ts +++ b/src/testRunner/tests.ts @@ -6,6 +6,7 @@ import "./unittests/comments"; import "./unittests/compilerCore"; import "./unittests/convertToBase64"; import "./unittests/customTransforms"; +import "./unittests/diagnosticCollection"; import "./unittests/factory"; import "./unittests/incrementalParser"; import "./unittests/jsDocParsing"; diff --git a/src/testRunner/unittests/diagnosticCollection.ts b/src/testRunner/unittests/diagnosticCollection.ts new file mode 100644 index 0000000000000..e0b543ac31272 --- /dev/null +++ b/src/testRunner/unittests/diagnosticCollection.ts @@ -0,0 +1,120 @@ +import * as ts from "../_namespaces/ts"; + +describe("unittests:: internalApi:: diagnosticCollection", () => { + describe("add", () => { + it("keeps equivalent diagnostic with elaboration", () => { + const collection = ts.createDiagnosticCollection(); + const file = ts.createSourceFile("index.ts", "const x = 1", ts.ScriptTarget.ESNext, /*setParentNodes*/ true); + const node = file.statements[0]; + + const dy = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "y"); + const da = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "a"); + collection.add(dy); + collection.add(da); + + const chain = ts.chainDiagnosticMessages( + ts.chainDiagnosticMessages(/*details*/ undefined, ts.Diagnostics.Did_you_mean_0, "x"), + ts.Diagnostics.Cannot_find_name_0, + "y", + ); + const dyBetter = ts.createDiagnosticForNodeFromMessageChain(file, node, chain); + + collection.add(dyBetter); + const result = collection.getDiagnostics(); + assert.deepEqual(result, [da, dyBetter]); + }); + + it("keeps equivalent diagnostic with deeper elaboration", () => { + const collection = ts.createDiagnosticCollection(); + const file = ts.createSourceFile("index.ts", "const x = 1", ts.ScriptTarget.ESNext, /*setParentNodes*/ true); + const node = file.statements[0]; + + const da = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "a"); + collection.add(da); + + const chain = ts.chainDiagnosticMessages( + ts.chainDiagnosticMessages(/*details*/ undefined, ts.Diagnostics.Did_you_mean_0, "a"), + ts.Diagnostics.Cannot_find_name_0, + "y", + ); + const dy = ts.createDiagnosticForNodeFromMessageChain(file, node, chain); + collection.add(dy); + + const chainBetter = ts.chainDiagnosticMessages( + ts.chainDiagnosticMessages( + ts.chainDiagnosticMessages(/*details*/ undefined, ts.Diagnostics.Did_you_mean_0, "x"), + ts.Diagnostics.Did_you_mean_0, + "b", + ), + ts.Diagnostics.Cannot_find_name_0, + "y", + ); + const dyBetter = ts.createDiagnosticForNodeFromMessageChain(file, node, chainBetter); + + collection.add(dyBetter); + const result = collection.getDiagnostics(); + assert.deepEqual(result, [da, dyBetter]); + }); + + it("doesn't keep equivalent diagnostic with no elaboration", () => { + const collection = ts.createDiagnosticCollection(); + const file = ts.createSourceFile("index.ts", "const x = 1", ts.ScriptTarget.ESNext, /*setParentNodes*/ true); + const node = file.statements[0]; + + const chain = ts.chainDiagnosticMessages( + ts.chainDiagnosticMessages(/*details*/ undefined, ts.Diagnostics.Did_you_mean_0, "x"), + ts.Diagnostics.Cannot_find_name_0, + "y", + ); + const dyBetter = ts.createDiagnosticForNodeFromMessageChain(file, node, chain); + const da = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "a"); + collection.add(da); + collection.add(dyBetter); + + const dy = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "y"); + collection.add(dy); + const result = collection.getDiagnostics(); + assert.deepEqual(result, [da, dyBetter]); + }); + }); + + describe("lookup", () => { + it("returns an equivalent diagnostic with more elaboration", () => { + const collection = ts.createDiagnosticCollection(); + const file = ts.createSourceFile("index.ts", "const x = 1", ts.ScriptTarget.ESNext, /*setParentNodes*/ true); + const node = file.statements[0]; + + const da = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "a"); + collection.add(da); + + const chain = ts.chainDiagnosticMessages( + ts.chainDiagnosticMessages(/*details*/ undefined, ts.Diagnostics.Did_you_mean_0, "x"), + ts.Diagnostics.Cannot_find_name_0, + "y", + ); + const dyBetter = ts.createDiagnosticForNodeFromMessageChain(file, node, chain); + collection.add(dyBetter); + + const dy = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "y"); + assert.equal(collection.lookup(dy), dyBetter); + }); + it("doesn't return an equivalent diagnostic with less elaboration", () => { + const collection = ts.createDiagnosticCollection(); + const file = ts.createSourceFile("index.ts", "const x = 1", ts.ScriptTarget.ESNext, /*setParentNodes*/ true); + const node = file.statements[0]; + + const da = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "a"); + collection.add(da); + const dy = ts.createDiagnosticForNode(node, ts.Diagnostics.Cannot_find_name_0, "y"); + collection.add(dy); + + const chain = ts.chainDiagnosticMessages( + ts.chainDiagnosticMessages(/*details*/ undefined, ts.Diagnostics.Did_you_mean_0, "x"), + ts.Diagnostics.Cannot_find_name_0, + "y", + ); + const dyBetter = ts.createDiagnosticForNodeFromMessageChain(file, node, chain); + assert.equal(collection.lookup(dyBetter), undefined); + }); + }); +}); diff --git a/tests/baselines/reference/destructuringAssignmentWithDefault2.errors.txt b/tests/baselines/reference/destructuringAssignmentWithDefault2.errors.txt index 0ed052c3841fb..dfd86cffc5b0a 100644 --- a/tests/baselines/reference/destructuringAssignmentWithDefault2.errors.txt +++ b/tests/baselines/reference/destructuringAssignmentWithDefault2.errors.txt @@ -1,6 +1,6 @@ -destructuringAssignmentWithDefault2.ts(11,4): error TS2322: Type 'undefined' is not assignable to type 'number'. destructuringAssignmentWithDefault2.ts(11,4): error TS2322: Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'. +destructuringAssignmentWithDefault2.ts(11,4): error TS2322: Type 'undefined' is not assignable to type 'number'. destructuringAssignmentWithDefault2.ts(12,7): error TS2322: Type 'undefined' is not assignable to type 'number'. destructuringAssignmentWithDefault2.ts(13,7): error TS2322: Type 'undefined' is not assignable to type 'number'. @@ -18,10 +18,10 @@ destructuringAssignmentWithDefault2.ts(13,7): error TS2322: Type 'undefined' is // Should be error ({ x = undefined } = a); ~ -!!! error TS2322: Type 'undefined' is not assignable to type 'number'. - ~ !!! error TS2322: Type 'number | undefined' is not assignable to type 'number'. !!! error TS2322: Type 'undefined' is not assignable to type 'number'. + ~ +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. ({ x: x = undefined } = a); ~ !!! error TS2322: Type 'undefined' is not assignable to type 'number'. diff --git a/tests/baselines/reference/duplicateErrorClassExpression.errors.txt b/tests/baselines/reference/duplicateErrorClassExpression.errors.txt new file mode 100644 index 0000000000000..55410e23191f8 --- /dev/null +++ b/tests/baselines/reference/duplicateErrorClassExpression.errors.txt @@ -0,0 +1,36 @@ +duplicateErrorClassExpression.ts(17,5): error TS2416: Property 'foo' in type 'Derived' is not assignable to the same property in base type 'Base'. + Type 'ComplicatedTypeDerived' is not assignable to type 'ComplicatedTypeBase'. + 'string' index signatures are incompatible. + Property 'a' is missing in type 'ADerived' but required in type 'ABase'. +duplicateErrorClassExpression.ts(20,5): error TS2538: Type 'typeof Derived' cannot be used as an index type. + + +==== duplicateErrorClassExpression.ts (2 errors) ==== + interface ComplicatedTypeBase { + [s: string]: ABase; + } + interface ComplicatedTypeDerived { + [s: string]: ADerived; + } + interface ABase { + a: string; + } + interface ADerived { + b: string; + } + class Base { + foo!: ComplicatedTypeBase; + } + const x = class Derived extends Base { + foo!: ComplicatedTypeDerived; + ~~~ +!!! error TS2416: Property 'foo' in type 'Derived' is not assignable to the same property in base type 'Base'. +!!! error TS2416: Type 'ComplicatedTypeDerived' is not assignable to type 'ComplicatedTypeBase'. +!!! error TS2416: 'string' index signatures are incompatible. +!!! error TS2416: Property 'a' is missing in type 'ADerived' but required in type 'ABase'. +!!! related TS2728 duplicateErrorClassExpression.ts:8:5: 'a' is declared here. + } + let obj: { 3: string } = { 3: "three" }; + obj[x]; + ~ +!!! error TS2538: Type 'typeof Derived' cannot be used as an index type. \ No newline at end of file diff --git a/tests/baselines/reference/duplicateErrorClassExpression.js b/tests/baselines/reference/duplicateErrorClassExpression.js new file mode 100644 index 0000000000000..7b1f341d23449 --- /dev/null +++ b/tests/baselines/reference/duplicateErrorClassExpression.js @@ -0,0 +1,55 @@ +//// [tests/cases/compiler/duplicateErrorClassExpression.ts] //// + +//// [duplicateErrorClassExpression.ts] +interface ComplicatedTypeBase { + [s: string]: ABase; +} +interface ComplicatedTypeDerived { + [s: string]: ADerived; +} +interface ABase { + a: string; +} +interface ADerived { + b: string; +} +class Base { + foo!: ComplicatedTypeBase; +} +const x = class Derived extends Base { + foo!: ComplicatedTypeDerived; +} +let obj: { 3: string } = { 3: "three" }; +obj[x]; + +//// [duplicateErrorClassExpression.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var Base = /** @class */ (function () { + function Base() { + } + return Base; +}()); +var x = /** @class */ (function (_super) { + __extends(Derived, _super); + function Derived() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Derived; +}(Base)); +var obj = { 3: "three" }; +obj[x]; diff --git a/tests/baselines/reference/duplicateErrorClassExpression.symbols b/tests/baselines/reference/duplicateErrorClassExpression.symbols new file mode 100644 index 0000000000000..9d672179a8ca9 --- /dev/null +++ b/tests/baselines/reference/duplicateErrorClassExpression.symbols @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/duplicateErrorClassExpression.ts] //// + +=== duplicateErrorClassExpression.ts === +interface ComplicatedTypeBase { +>ComplicatedTypeBase : Symbol(ComplicatedTypeBase, Decl(duplicateErrorClassExpression.ts, 0, 0)) + + [s: string]: ABase; +>s : Symbol(s, Decl(duplicateErrorClassExpression.ts, 1, 5)) +>ABase : Symbol(ABase, Decl(duplicateErrorClassExpression.ts, 5, 1)) +} +interface ComplicatedTypeDerived { +>ComplicatedTypeDerived : Symbol(ComplicatedTypeDerived, Decl(duplicateErrorClassExpression.ts, 2, 1)) + + [s: string]: ADerived; +>s : Symbol(s, Decl(duplicateErrorClassExpression.ts, 4, 5)) +>ADerived : Symbol(ADerived, Decl(duplicateErrorClassExpression.ts, 8, 1)) +} +interface ABase { +>ABase : Symbol(ABase, Decl(duplicateErrorClassExpression.ts, 5, 1)) + + a: string; +>a : Symbol(ABase.a, Decl(duplicateErrorClassExpression.ts, 6, 17)) +} +interface ADerived { +>ADerived : Symbol(ADerived, Decl(duplicateErrorClassExpression.ts, 8, 1)) + + b: string; +>b : Symbol(ADerived.b, Decl(duplicateErrorClassExpression.ts, 9, 20)) +} +class Base { +>Base : Symbol(Base, Decl(duplicateErrorClassExpression.ts, 11, 1)) + + foo!: ComplicatedTypeBase; +>foo : Symbol(Base.foo, Decl(duplicateErrorClassExpression.ts, 12, 12)) +>ComplicatedTypeBase : Symbol(ComplicatedTypeBase, Decl(duplicateErrorClassExpression.ts, 0, 0)) +} +const x = class Derived extends Base { +>x : Symbol(x, Decl(duplicateErrorClassExpression.ts, 15, 5)) +>Derived : Symbol(Derived, Decl(duplicateErrorClassExpression.ts, 15, 9)) +>Base : Symbol(Base, Decl(duplicateErrorClassExpression.ts, 11, 1)) + + foo!: ComplicatedTypeDerived; +>foo : Symbol(Derived.foo, Decl(duplicateErrorClassExpression.ts, 15, 38)) +>ComplicatedTypeDerived : Symbol(ComplicatedTypeDerived, Decl(duplicateErrorClassExpression.ts, 2, 1)) +} +let obj: { 3: string } = { 3: "three" }; +>obj : Symbol(obj, Decl(duplicateErrorClassExpression.ts, 18, 3)) +>3 : Symbol(3, Decl(duplicateErrorClassExpression.ts, 18, 10)) +>3 : Symbol(3, Decl(duplicateErrorClassExpression.ts, 18, 26)) + +obj[x]; +>obj : Symbol(obj, Decl(duplicateErrorClassExpression.ts, 18, 3)) +>x : Symbol(x, Decl(duplicateErrorClassExpression.ts, 15, 5)) + diff --git a/tests/baselines/reference/duplicateErrorClassExpression.types b/tests/baselines/reference/duplicateErrorClassExpression.types new file mode 100644 index 0000000000000..69a573ec7e28c --- /dev/null +++ b/tests/baselines/reference/duplicateErrorClassExpression.types @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/duplicateErrorClassExpression.ts] //// + +=== duplicateErrorClassExpression.ts === +interface ComplicatedTypeBase { + [s: string]: ABase; +>s : string +> : ^^^^^^ +} +interface ComplicatedTypeDerived { + [s: string]: ADerived; +>s : string +> : ^^^^^^ +} +interface ABase { + a: string; +>a : string +> : ^^^^^^ +} +interface ADerived { + b: string; +>b : string +> : ^^^^^^ +} +class Base { +>Base : Base +> : ^^^^ + + foo!: ComplicatedTypeBase; +>foo : ComplicatedTypeBase +> : ^^^^^^^^^^^^^^^^^^^ +} +const x = class Derived extends Base { +>x : typeof Derived +> : ^^^^^^^^^^^^^^ +>class Derived extends Base { foo!: ComplicatedTypeDerived;} : typeof Derived +> : ^^^^^^^^^^^^^^ +>Derived : typeof Derived +> : ^^^^^^^^^^^^^^ +>Base : Base +> : ^^^^ + + foo!: ComplicatedTypeDerived; +>foo : ComplicatedTypeDerived +> : ^^^^^^^^^^^^^^^^^^^^^^ +} +let obj: { 3: string } = { 3: "three" }; +>obj : { 3: string; } +> : ^^^^^ ^^^ +>3 : string +> : ^^^^^^ +>{ 3: "three" } : { 3: string; } +> : ^^^^^^^^^^^^^^ +>3 : string +> : ^^^^^^ +>"three" : "three" +> : ^^^^^^^ + +obj[x]; +>obj[x] : any +> : ^^^ +>obj : { 3: string; } +> : ^^^^^^^^^^^^^^ +>x : typeof Derived +> : ^^^^^^^^^^^^^^ + diff --git a/tests/baselines/reference/multipleDefaultExports05.errors.txt b/tests/baselines/reference/multipleDefaultExports05.errors.txt index 4e89136ef8541..3f07b6e127951 100644 --- a/tests/baselines/reference/multipleDefaultExports05.errors.txt +++ b/tests/baselines/reference/multipleDefaultExports05.errors.txt @@ -1,17 +1,13 @@ multipleDefaultExports05.ts(1,22): error TS2528: A module cannot have multiple default exports. -multipleDefaultExports05.ts(1,22): error TS2528: A module cannot have multiple default exports. multipleDefaultExports05.ts(3,22): error TS2528: A module cannot have multiple default exports. multipleDefaultExports05.ts(5,22): error TS2528: A module cannot have multiple default exports. -==== multipleDefaultExports05.ts (4 errors) ==== +==== multipleDefaultExports05.ts (3 errors) ==== export default class AA1 {} ~~~ !!! error TS2528: A module cannot have multiple default exports. !!! related TS2753 multipleDefaultExports05.ts:3:22: Another export default is here. - ~~~ -!!! error TS2528: A module cannot have multiple default exports. -!!! related TS2753 multipleDefaultExports05.ts:5:22: Another export default is here. export default class BB1 {} ~~~ diff --git a/tests/cases/compiler/duplicateErrorClassExpression.ts b/tests/cases/compiler/duplicateErrorClassExpression.ts new file mode 100644 index 0000000000000..07c569bf70467 --- /dev/null +++ b/tests/cases/compiler/duplicateErrorClassExpression.ts @@ -0,0 +1,22 @@ +// @strict: true + +interface ComplicatedTypeBase { + [s: string]: ABase; +} +interface ComplicatedTypeDerived { + [s: string]: ADerived; +} +interface ABase { + a: string; +} +interface ADerived { + b: string; +} +class Base { + foo!: ComplicatedTypeBase; +} +const x = class Derived extends Base { + foo!: ComplicatedTypeDerived; +} +let obj: { 3: string } = { 3: "three" }; +obj[x]; \ No newline at end of file