Skip to content

Commit 484f141

Browse files
authored
Prevent merged class/namespace from overlapping with Record<string, unknown> (microsoft#47088)
1 parent a3d23d3 commit 484f141

6 files changed

+189
-3
lines changed

Diff for: src/compiler/checker.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -21036,9 +21036,14 @@ namespace ts {
2103621036
* with no call or construct signatures.
2103721037
*/
2103821038
function isObjectTypeWithInferableIndex(type: Type): boolean {
21039-
return type.flags & TypeFlags.Intersection ? every((type as IntersectionType).types, isObjectTypeWithInferableIndex) :
21040-
!!(type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0 &&
21041-
!typeHasCallOrConstructSignatures(type)) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source));
21039+
return type.flags & TypeFlags.Intersection
21040+
? every((type as IntersectionType).types, isObjectTypeWithInferableIndex)
21041+
: !!(
21042+
type.symbol
21043+
&& (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0
21044+
&& !(type.symbol.flags & SymbolFlags.Class)
21045+
&& !typeHasCallOrConstructSignatures(type)
21046+
) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source));
2104221047
}
2104321048

2104421049
function createSymbolWithType(source: Symbol, type: Type | undefined) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
tests/cases/compiler/mergedClassNamespaceRecordCast.ts(3,1): error TS2352: Conversion of type 'C1' to type 'Record<string, unknown>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
2+
Index signature for type 'string' is missing in type 'C1'.
3+
tests/cases/compiler/mergedClassNamespaceRecordCast.ts(9,1): error TS2352: Conversion of type 'C2' to type 'Record<string, unknown>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
4+
Index signature for type 'string' is missing in type 'C2'.
5+
tests/cases/compiler/mergedClassNamespaceRecordCast.ts(12,10): error TS2339: Property 'unrelated' does not exist on type 'C2'.
6+
7+
8+
==== tests/cases/compiler/mergedClassNamespaceRecordCast.ts (3 errors) ====
9+
class C1 { foo() {} }
10+
11+
new C1() as Record<string, unknown>;
12+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
!!! error TS2352: Conversion of type 'C1' to type 'Record<string, unknown>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
14+
!!! error TS2352: Index signature for type 'string' is missing in type 'C1'.
15+
16+
17+
class C2 { foo() {} }
18+
namespace C2 { export const unrelated = 3; }
19+
20+
new C2() as Record<string, unknown>;
21+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22+
!!! error TS2352: Conversion of type 'C2' to type 'Record<string, unknown>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
23+
!!! error TS2352: Index signature for type 'string' is missing in type 'C2'.
24+
25+
C2.unrelated
26+
new C2().unrelated
27+
~~~~~~~~~
28+
!!! error TS2339: Property 'unrelated' does not exist on type 'C2'.
29+
30+
31+
namespace C3 { export const unrelated = 3; }
32+
33+
C3 as Record<string, unknown>;
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//// [mergedClassNamespaceRecordCast.ts]
2+
class C1 { foo() {} }
3+
4+
new C1() as Record<string, unknown>;
5+
6+
7+
class C2 { foo() {} }
8+
namespace C2 { export const unrelated = 3; }
9+
10+
new C2() as Record<string, unknown>;
11+
12+
C2.unrelated
13+
new C2().unrelated
14+
15+
16+
namespace C3 { export const unrelated = 3; }
17+
18+
C3 as Record<string, unknown>;
19+
20+
21+
//// [mergedClassNamespaceRecordCast.js]
22+
var C1 = /** @class */ (function () {
23+
function C1() {
24+
}
25+
C1.prototype.foo = function () { };
26+
return C1;
27+
}());
28+
new C1();
29+
var C2 = /** @class */ (function () {
30+
function C2() {
31+
}
32+
C2.prototype.foo = function () { };
33+
return C2;
34+
}());
35+
(function (C2) {
36+
C2.unrelated = 3;
37+
})(C2 || (C2 = {}));
38+
new C2();
39+
C2.unrelated;
40+
new C2().unrelated;
41+
var C3;
42+
(function (C3) {
43+
C3.unrelated = 3;
44+
})(C3 || (C3 = {}));
45+
C3;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/compiler/mergedClassNamespaceRecordCast.ts ===
2+
class C1 { foo() {} }
3+
>C1 : Symbol(C1, Decl(mergedClassNamespaceRecordCast.ts, 0, 0))
4+
>foo : Symbol(C1.foo, Decl(mergedClassNamespaceRecordCast.ts, 0, 10))
5+
6+
new C1() as Record<string, unknown>;
7+
>C1 : Symbol(C1, Decl(mergedClassNamespaceRecordCast.ts, 0, 0))
8+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
9+
10+
11+
class C2 { foo() {} }
12+
>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21))
13+
>foo : Symbol(C2.foo, Decl(mergedClassNamespaceRecordCast.ts, 5, 10))
14+
15+
namespace C2 { export const unrelated = 3; }
16+
>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21))
17+
>unrelated : Symbol(unrelated, Decl(mergedClassNamespaceRecordCast.ts, 6, 27))
18+
19+
new C2() as Record<string, unknown>;
20+
>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21))
21+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
22+
23+
C2.unrelated
24+
>C2.unrelated : Symbol(C2.unrelated, Decl(mergedClassNamespaceRecordCast.ts, 6, 27))
25+
>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21))
26+
>unrelated : Symbol(C2.unrelated, Decl(mergedClassNamespaceRecordCast.ts, 6, 27))
27+
28+
new C2().unrelated
29+
>C2 : Symbol(C2, Decl(mergedClassNamespaceRecordCast.ts, 2, 36), Decl(mergedClassNamespaceRecordCast.ts, 5, 21))
30+
31+
32+
namespace C3 { export const unrelated = 3; }
33+
>C3 : Symbol(C3, Decl(mergedClassNamespaceRecordCast.ts, 11, 18))
34+
>unrelated : Symbol(unrelated, Decl(mergedClassNamespaceRecordCast.ts, 14, 27))
35+
36+
C3 as Record<string, unknown>;
37+
>C3 : Symbol(C3, Decl(mergedClassNamespaceRecordCast.ts, 11, 18))
38+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
39+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
=== tests/cases/compiler/mergedClassNamespaceRecordCast.ts ===
2+
class C1 { foo() {} }
3+
>C1 : C1
4+
>foo : () => void
5+
6+
new C1() as Record<string, unknown>;
7+
>new C1() as Record<string, unknown> : Record<string, unknown>
8+
>new C1() : C1
9+
>C1 : typeof C1
10+
11+
12+
class C2 { foo() {} }
13+
>C2 : C2
14+
>foo : () => void
15+
16+
namespace C2 { export const unrelated = 3; }
17+
>C2 : typeof C2
18+
>unrelated : 3
19+
>3 : 3
20+
21+
new C2() as Record<string, unknown>;
22+
>new C2() as Record<string, unknown> : Record<string, unknown>
23+
>new C2() : C2
24+
>C2 : typeof C2
25+
26+
C2.unrelated
27+
>C2.unrelated : 3
28+
>C2 : typeof C2
29+
>unrelated : 3
30+
31+
new C2().unrelated
32+
>new C2().unrelated : any
33+
>new C2() : C2
34+
>C2 : typeof C2
35+
>unrelated : any
36+
37+
38+
namespace C3 { export const unrelated = 3; }
39+
>C3 : typeof C3
40+
>unrelated : 3
41+
>3 : 3
42+
43+
C3 as Record<string, unknown>;
44+
>C3 as Record<string, unknown> : Record<string, unknown>
45+
>C3 : typeof C3
46+
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class C1 { foo() {} }
2+
3+
new C1() as Record<string, unknown>;
4+
5+
6+
class C2 { foo() {} }
7+
namespace C2 { export const unrelated = 3; }
8+
9+
new C2() as Record<string, unknown>;
10+
11+
C2.unrelated
12+
new C2().unrelated
13+
14+
15+
namespace C3 { export const unrelated = 3; }
16+
17+
C3 as Record<string, unknown>;

0 commit comments

Comments
 (0)