From dab759a72500fea5f99dc1aa24e8ed11032cade9 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 8 Dec 2021 21:15:05 -0800 Subject: [PATCH 1/3] Add baseline incorrect test --- .../mergedClassNamespaceRecordCast.errors.txt | 29 ++++++++++++ .../mergedClassNamespaceRecordCast.js | 45 ++++++++++++++++++ .../mergedClassNamespaceRecordCast.symbols | 39 ++++++++++++++++ .../mergedClassNamespaceRecordCast.types | 46 +++++++++++++++++++ .../mergedClassNamespaceRecordCast.ts | 17 +++++++ 5 files changed, 176 insertions(+) create mode 100644 tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt create mode 100644 tests/baselines/reference/mergedClassNamespaceRecordCast.js create mode 100644 tests/baselines/reference/mergedClassNamespaceRecordCast.symbols create mode 100644 tests/baselines/reference/mergedClassNamespaceRecordCast.types create mode 100644 tests/cases/compiler/mergedClassNamespaceRecordCast.ts diff --git a/tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt b/tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt new file mode 100644 index 0000000000000..eb39b0d830bf7 --- /dev/null +++ b/tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/mergedClassNamespaceRecordCast.ts(3,1): error TS2352: Conversion of type 'C1' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. + Index signature for type 'string' is missing in type 'C1'. +tests/cases/compiler/mergedClassNamespaceRecordCast.ts(12,10): error TS2339: Property 'unrelated' does not exist on type 'C2'. + + +==== tests/cases/compiler/mergedClassNamespaceRecordCast.ts (2 errors) ==== + class C1 { foo() {} } + + new C1() as Record; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2352: Conversion of type 'C1' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. +!!! error TS2352: Index signature for type 'string' is missing in type 'C1'. + + + class C2 { foo() {} } + namespace C2 { export const unrelated = 3; } + + new C2() as Record; + + C2.unrelated + new C2().unrelated + ~~~~~~~~~ +!!! error TS2339: Property 'unrelated' does not exist on type 'C2'. + + + namespace C3 { export const unrelated = 3; } + + C3 as Record; + \ No newline at end of file diff --git a/tests/baselines/reference/mergedClassNamespaceRecordCast.js b/tests/baselines/reference/mergedClassNamespaceRecordCast.js new file mode 100644 index 0000000000000..848f01d70cfbb --- /dev/null +++ b/tests/baselines/reference/mergedClassNamespaceRecordCast.js @@ -0,0 +1,45 @@ +//// [mergedClassNamespaceRecordCast.ts] +class C1 { foo() {} } + +new C1() as Record; + + +class C2 { foo() {} } +namespace C2 { export const unrelated = 3; } + +new C2() as Record; + +C2.unrelated +new C2().unrelated + + +namespace C3 { export const unrelated = 3; } + +C3 as Record; + + +//// [mergedClassNamespaceRecordCast.js] +var C1 = /** @class */ (function () { + function C1() { + } + C1.prototype.foo = function () { }; + return C1; +}()); +new C1(); +var C2 = /** @class */ (function () { + function C2() { + } + C2.prototype.foo = function () { }; + return C2; +}()); +(function (C2) { + C2.unrelated = 3; +})(C2 || (C2 = {})); +new C2(); +C2.unrelated; +new C2().unrelated; +var C3; +(function (C3) { + C3.unrelated = 3; +})(C3 || (C3 = {})); +C3; diff --git a/tests/baselines/reference/mergedClassNamespaceRecordCast.symbols b/tests/baselines/reference/mergedClassNamespaceRecordCast.symbols new file mode 100644 index 0000000000000..71f6d7c79a54e --- /dev/null +++ b/tests/baselines/reference/mergedClassNamespaceRecordCast.symbols @@ -0,0 +1,39 @@ +=== tests/cases/compiler/mergedClassNamespaceRecordCast.ts === +class C1 { foo() {} } +>C1 : Symbol(C1, Decl(mergedClassNamespaceRecordCast.ts, 0, 0)) +>foo : Symbol(C1.foo, Decl(mergedClassNamespaceRecordCast.ts, 0, 10)) + +new C1() as Record; +>C1 : Symbol(C1, Decl(mergedClassNamespaceRecordCast.ts, 0, 0)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + +class C2 { foo() {} } +>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21)) +>foo : Symbol(C2.foo, Decl(mergedClassNamespaceRecordCast.ts, 5, 10)) + +namespace C2 { export const unrelated = 3; } +>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21)) +>unrelated : Symbol(unrelated, Decl(mergedClassNamespaceRecordCast.ts, 6, 27)) + +new C2() as Record; +>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +C2.unrelated +>C2.unrelated : Symbol(C2.unrelated, Decl(mergedClassNamespaceRecordCast.ts, 6, 27)) +>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21)) +>unrelated : Symbol(C2.unrelated, Decl(mergedClassNamespaceRecordCast.ts, 6, 27)) + +new C2().unrelated +>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21)) + + +namespace C3 { export const unrelated = 3; } +>C3 : Symbol(C3, Decl(mergedClassNamespaceRecordCast.ts, 11, 18)) +>unrelated : Symbol(unrelated, Decl(mergedClassNamespaceRecordCast.ts, 14, 27)) + +C3 as Record; +>C3 : Symbol(C3, Decl(mergedClassNamespaceRecordCast.ts, 11, 18)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + diff --git a/tests/baselines/reference/mergedClassNamespaceRecordCast.types b/tests/baselines/reference/mergedClassNamespaceRecordCast.types new file mode 100644 index 0000000000000..8d15fe5151291 --- /dev/null +++ b/tests/baselines/reference/mergedClassNamespaceRecordCast.types @@ -0,0 +1,46 @@ +=== tests/cases/compiler/mergedClassNamespaceRecordCast.ts === +class C1 { foo() {} } +>C1 : C1 +>foo : () => void + +new C1() as Record; +>new C1() as Record : Record +>new C1() : C1 +>C1 : typeof C1 + + +class C2 { foo() {} } +>C2 : C2 +>foo : () => void + +namespace C2 { export const unrelated = 3; } +>C2 : typeof C2 +>unrelated : 3 +>3 : 3 + +new C2() as Record; +>new C2() as Record : Record +>new C2() : C2 +>C2 : typeof C2 + +C2.unrelated +>C2.unrelated : 3 +>C2 : typeof C2 +>unrelated : 3 + +new C2().unrelated +>new C2().unrelated : any +>new C2() : C2 +>C2 : typeof C2 +>unrelated : any + + +namespace C3 { export const unrelated = 3; } +>C3 : typeof C3 +>unrelated : 3 +>3 : 3 + +C3 as Record; +>C3 as Record : Record +>C3 : typeof C3 + diff --git a/tests/cases/compiler/mergedClassNamespaceRecordCast.ts b/tests/cases/compiler/mergedClassNamespaceRecordCast.ts new file mode 100644 index 0000000000000..830cbc4e4858b --- /dev/null +++ b/tests/cases/compiler/mergedClassNamespaceRecordCast.ts @@ -0,0 +1,17 @@ +class C1 { foo() {} } + +new C1() as Record; + + +class C2 { foo() {} } +namespace C2 { export const unrelated = 3; } + +new C2() as Record; + +C2.unrelated +new C2().unrelated + + +namespace C3 { export const unrelated = 3; } + +C3 as Record; From 01b49f09d1cfc750a9d512af2ff929ac227ab62c Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 9 Dec 2021 09:57:46 -0800 Subject: [PATCH 2/3] Don't treat objects that are both classes and module values as having inferrable indexes --- src/compiler/checker.ts | 11 ++++++++--- .../mergedClassNamespaceRecordCast.errors.txt | 7 ++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ada97551aacc..03c257c11036c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21000,9 +21000,14 @@ namespace ts { * with no call or construct signatures. */ function isObjectTypeWithInferableIndex(type: Type): boolean { - return type.flags & TypeFlags.Intersection ? every((type as IntersectionType).types, isObjectTypeWithInferableIndex) : - !!(type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0 && - !typeHasCallOrConstructSignatures(type)) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source)); + return type.flags & TypeFlags.Intersection + ? every((type as IntersectionType).types, isObjectTypeWithInferableIndex) + : !!( + type.symbol + && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0 + && !((type.symbol.flags & SymbolFlags.Class) && (type.symbol.flags & SymbolFlags.ValueModule)) + && !typeHasCallOrConstructSignatures(type) + ) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source)); } function createSymbolWithType(source: Symbol, type: Type | undefined) { diff --git a/tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt b/tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt index eb39b0d830bf7..7c170f7e7cc33 100644 --- a/tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt +++ b/tests/baselines/reference/mergedClassNamespaceRecordCast.errors.txt @@ -1,9 +1,11 @@ tests/cases/compiler/mergedClassNamespaceRecordCast.ts(3,1): error TS2352: Conversion of type 'C1' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Index signature for type 'string' is missing in type 'C1'. +tests/cases/compiler/mergedClassNamespaceRecordCast.ts(9,1): error TS2352: Conversion of type 'C2' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. + Index signature for type 'string' is missing in type 'C2'. tests/cases/compiler/mergedClassNamespaceRecordCast.ts(12,10): error TS2339: Property 'unrelated' does not exist on type 'C2'. -==== tests/cases/compiler/mergedClassNamespaceRecordCast.ts (2 errors) ==== +==== tests/cases/compiler/mergedClassNamespaceRecordCast.ts (3 errors) ==== class C1 { foo() {} } new C1() as Record; @@ -16,6 +18,9 @@ tests/cases/compiler/mergedClassNamespaceRecordCast.ts(12,10): error TS2339: Pro namespace C2 { export const unrelated = 3; } new C2() as Record; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2352: Conversion of type 'C2' to type 'Record' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. +!!! error TS2352: Index signature for type 'string' is missing in type 'C2'. C2.unrelated new C2().unrelated From cba34c0c5a3051370de8fa2551aa0374c3a66b12 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 7 Jan 2022 10:40:47 -0800 Subject: [PATCH 3/3] Just check for class --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 03c257c11036c..23f28f98be623 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21005,7 +21005,7 @@ namespace ts { : !!( type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0 - && !((type.symbol.flags & SymbolFlags.Class) && (type.symbol.flags & SymbolFlags.ValueModule)) + && !(type.symbol.flags & SymbolFlags.Class) && !typeHasCallOrConstructSignatures(type) ) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source)); }