Skip to content

Commit 50a5ea5

Browse files
committed
Fix microsoft#18545, dont use declared type of class expression
1 parent de0e475 commit 50a5ea5

6 files changed

Lines changed: 345 additions & 1 deletion

File tree

‎src/compiler/checker.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,11 @@ namespace ts {
27162716
context.encounteredError = true;
27172717
return undefined;
27182718
}
2719+
else if (context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral &&
2720+
type.symbol.valueDeclaration &&
2721+
type.symbol.valueDeclaration.kind === SyntaxKind.ClassExpression) {
2722+
return createAnonymousTypeNode(type);
2723+
}
27192724
else {
27202725
const outerTypeParameters = type.target.outerTypeParameters;
27212726
let i = 0;
@@ -3398,7 +3403,7 @@ namespace ts {
33983403
else if (flags & TypeFormatFlags.WriteClassExpressionAsTypeLiteral &&
33993404
type.symbol.valueDeclaration &&
34003405
type.symbol.valueDeclaration.kind === SyntaxKind.ClassExpression) {
3401-
writeAnonymousType(getDeclaredTypeOfClassOrInterface(type.symbol), flags);
3406+
writeAnonymousType(type, flags);
34023407
}
34033408
else {
34043409
// Write the type reference in the format f<A>.g<B>.C<X, Y> where A and B are type arguments

‎src/compiler/types.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,7 @@ namespace ts {
27432743
// Options
27442744
NoTruncation = 1 << 0, // Don't truncate result
27452745
WriteArrayAsGenericType = 1 << 1, // Write Array<T> instead T[]
2746+
WriteClassExpressionAsTypeLiteral = 1 << 4, // Should write class expressions as type literals
27462747
WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature
27472748
UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
27482749
SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//// [declarationNoDanglingGenerics.ts]
2+
const kindCache: { [kind: string]: boolean } = {};
3+
4+
function register(kind: string): void | never {
5+
if (kindCache[kind]) {
6+
throw new Error(`Class with kind "${kind}" is already registered.`);
7+
}
8+
kindCache[kind] = true;
9+
}
10+
11+
function ClassFactory<TKind extends string>(kind: TKind) {
12+
register(kind);
13+
14+
return class {
15+
static readonly THE_KIND: TKind = kind;
16+
readonly kind: TKind = kind;
17+
};
18+
}
19+
20+
class Kinds {
21+
static readonly A = "A";
22+
static readonly B = "B";
23+
static readonly C = "C";
24+
}
25+
26+
export class AKind extends ClassFactory(Kinds.A) {
27+
}
28+
29+
export class BKind extends ClassFactory(Kinds.B) {
30+
}
31+
32+
export class CKind extends ClassFactory(Kinds.C) {
33+
}
34+
35+
//// [declarationNoDanglingGenerics.js]
36+
"use strict";
37+
var __extends = (this && this.__extends) || (function () {
38+
var extendStatics = Object.setPrototypeOf ||
39+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
40+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
41+
return function (d, b) {
42+
extendStatics(d, b);
43+
function __() { this.constructor = d; }
44+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
45+
};
46+
})();
47+
exports.__esModule = true;
48+
var kindCache = {};
49+
function register(kind) {
50+
if (kindCache[kind]) {
51+
throw new Error("Class with kind \"" + kind + "\" is already registered.");
52+
}
53+
kindCache[kind] = true;
54+
}
55+
function ClassFactory(kind) {
56+
register(kind);
57+
return _a = /** @class */ (function () {
58+
function class_1() {
59+
this.kind = kind;
60+
}
61+
return class_1;
62+
}()),
63+
_a.THE_KIND = kind,
64+
_a;
65+
var _a;
66+
}
67+
var Kinds = /** @class */ (function () {
68+
function Kinds() {
69+
}
70+
Kinds.A = "A";
71+
Kinds.B = "B";
72+
Kinds.C = "C";
73+
return Kinds;
74+
}());
75+
var AKind = /** @class */ (function (_super) {
76+
__extends(AKind, _super);
77+
function AKind() {
78+
return _super !== null && _super.apply(this, arguments) || this;
79+
}
80+
return AKind;
81+
}(ClassFactory(Kinds.A)));
82+
exports.AKind = AKind;
83+
var BKind = /** @class */ (function (_super) {
84+
__extends(BKind, _super);
85+
function BKind() {
86+
return _super !== null && _super.apply(this, arguments) || this;
87+
}
88+
return BKind;
89+
}(ClassFactory(Kinds.B)));
90+
exports.BKind = BKind;
91+
var CKind = /** @class */ (function (_super) {
92+
__extends(CKind, _super);
93+
function CKind() {
94+
return _super !== null && _super.apply(this, arguments) || this;
95+
}
96+
return CKind;
97+
}(ClassFactory(Kinds.C)));
98+
exports.CKind = CKind;
99+
100+
101+
//// [declarationNoDanglingGenerics.d.ts]
102+
declare const AKind_base: {
103+
new (): {
104+
readonly kind: "A";
105+
};
106+
readonly THE_KIND: "A";
107+
};
108+
export declare class AKind extends AKind_base {
109+
}
110+
declare const BKind_base: {
111+
new (): {
112+
readonly kind: "B";
113+
};
114+
readonly THE_KIND: "B";
115+
};
116+
export declare class BKind extends BKind_base {
117+
}
118+
declare const CKind_base: {
119+
new (): {
120+
readonly kind: "C";
121+
};
122+
readonly THE_KIND: "C";
123+
};
124+
export declare class CKind extends CKind_base {
125+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
=== tests/cases/compiler/declarationNoDanglingGenerics.ts ===
2+
const kindCache: { [kind: string]: boolean } = {};
3+
>kindCache : Symbol(kindCache, Decl(declarationNoDanglingGenerics.ts, 0, 5))
4+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 0, 20))
5+
6+
function register(kind: string): void | never {
7+
>register : Symbol(register, Decl(declarationNoDanglingGenerics.ts, 0, 50))
8+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 2, 18))
9+
10+
if (kindCache[kind]) {
11+
>kindCache : Symbol(kindCache, Decl(declarationNoDanglingGenerics.ts, 0, 5))
12+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 2, 18))
13+
14+
throw new Error(`Class with kind "${kind}" is already registered.`);
15+
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
16+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 2, 18))
17+
}
18+
kindCache[kind] = true;
19+
>kindCache : Symbol(kindCache, Decl(declarationNoDanglingGenerics.ts, 0, 5))
20+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 2, 18))
21+
}
22+
23+
function ClassFactory<TKind extends string>(kind: TKind) {
24+
>ClassFactory : Symbol(ClassFactory, Decl(declarationNoDanglingGenerics.ts, 7, 1))
25+
>TKind : Symbol(TKind, Decl(declarationNoDanglingGenerics.ts, 9, 22))
26+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 9, 44))
27+
>TKind : Symbol(TKind, Decl(declarationNoDanglingGenerics.ts, 9, 22))
28+
29+
register(kind);
30+
>register : Symbol(register, Decl(declarationNoDanglingGenerics.ts, 0, 50))
31+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 9, 44))
32+
33+
return class {
34+
static readonly THE_KIND: TKind = kind;
35+
>THE_KIND : Symbol((Anonymous class).THE_KIND, Decl(declarationNoDanglingGenerics.ts, 12, 16))
36+
>TKind : Symbol(TKind, Decl(declarationNoDanglingGenerics.ts, 9, 22))
37+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 9, 44))
38+
39+
readonly kind: TKind = kind;
40+
>kind : Symbol((Anonymous class).kind, Decl(declarationNoDanglingGenerics.ts, 13, 43))
41+
>TKind : Symbol(TKind, Decl(declarationNoDanglingGenerics.ts, 9, 22))
42+
>kind : Symbol(kind, Decl(declarationNoDanglingGenerics.ts, 9, 44))
43+
44+
};
45+
}
46+
47+
class Kinds {
48+
>Kinds : Symbol(Kinds, Decl(declarationNoDanglingGenerics.ts, 16, 1))
49+
50+
static readonly A = "A";
51+
>A : Symbol(Kinds.A, Decl(declarationNoDanglingGenerics.ts, 18, 13))
52+
53+
static readonly B = "B";
54+
>B : Symbol(Kinds.B, Decl(declarationNoDanglingGenerics.ts, 19, 26))
55+
56+
static readonly C = "C";
57+
>C : Symbol(Kinds.C, Decl(declarationNoDanglingGenerics.ts, 20, 26))
58+
}
59+
60+
export class AKind extends ClassFactory(Kinds.A) {
61+
>AKind : Symbol(AKind, Decl(declarationNoDanglingGenerics.ts, 22, 1))
62+
>ClassFactory : Symbol(ClassFactory, Decl(declarationNoDanglingGenerics.ts, 7, 1))
63+
>Kinds.A : Symbol(Kinds.A, Decl(declarationNoDanglingGenerics.ts, 18, 13))
64+
>Kinds : Symbol(Kinds, Decl(declarationNoDanglingGenerics.ts, 16, 1))
65+
>A : Symbol(Kinds.A, Decl(declarationNoDanglingGenerics.ts, 18, 13))
66+
}
67+
68+
export class BKind extends ClassFactory(Kinds.B) {
69+
>BKind : Symbol(BKind, Decl(declarationNoDanglingGenerics.ts, 25, 1))
70+
>ClassFactory : Symbol(ClassFactory, Decl(declarationNoDanglingGenerics.ts, 7, 1))
71+
>Kinds.B : Symbol(Kinds.B, Decl(declarationNoDanglingGenerics.ts, 19, 26))
72+
>Kinds : Symbol(Kinds, Decl(declarationNoDanglingGenerics.ts, 16, 1))
73+
>B : Symbol(Kinds.B, Decl(declarationNoDanglingGenerics.ts, 19, 26))
74+
}
75+
76+
export class CKind extends ClassFactory(Kinds.C) {
77+
>CKind : Symbol(CKind, Decl(declarationNoDanglingGenerics.ts, 28, 1))
78+
>ClassFactory : Symbol(ClassFactory, Decl(declarationNoDanglingGenerics.ts, 7, 1))
79+
>Kinds.C : Symbol(Kinds.C, Decl(declarationNoDanglingGenerics.ts, 20, 26))
80+
>Kinds : Symbol(Kinds, Decl(declarationNoDanglingGenerics.ts, 16, 1))
81+
>C : Symbol(Kinds.C, Decl(declarationNoDanglingGenerics.ts, 20, 26))
82+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
=== tests/cases/compiler/declarationNoDanglingGenerics.ts ===
2+
const kindCache: { [kind: string]: boolean } = {};
3+
>kindCache : { [kind: string]: boolean; }
4+
>kind : string
5+
>{} : {}
6+
7+
function register(kind: string): void | never {
8+
>register : (kind: string) => void
9+
>kind : string
10+
11+
if (kindCache[kind]) {
12+
>kindCache[kind] : boolean
13+
>kindCache : { [kind: string]: boolean; }
14+
>kind : string
15+
16+
throw new Error(`Class with kind "${kind}" is already registered.`);
17+
>new Error(`Class with kind "${kind}" is already registered.`) : Error
18+
>Error : ErrorConstructor
19+
>`Class with kind "${kind}" is already registered.` : string
20+
>kind : string
21+
}
22+
kindCache[kind] = true;
23+
>kindCache[kind] = true : true
24+
>kindCache[kind] : boolean
25+
>kindCache : { [kind: string]: boolean; }
26+
>kind : string
27+
>true : true
28+
}
29+
30+
function ClassFactory<TKind extends string>(kind: TKind) {
31+
>ClassFactory : <TKind extends string>(kind: TKind) => typeof (Anonymous class)
32+
>TKind : TKind
33+
>kind : TKind
34+
>TKind : TKind
35+
36+
register(kind);
37+
>register(kind) : void
38+
>register : (kind: string) => void
39+
>kind : TKind
40+
41+
return class {
42+
>class { static readonly THE_KIND: TKind = kind; readonly kind: TKind = kind; } : typeof (Anonymous class)
43+
44+
static readonly THE_KIND: TKind = kind;
45+
>THE_KIND : TKind
46+
>TKind : TKind
47+
>kind : TKind
48+
49+
readonly kind: TKind = kind;
50+
>kind : TKind
51+
>TKind : TKind
52+
>kind : TKind
53+
54+
};
55+
}
56+
57+
class Kinds {
58+
>Kinds : Kinds
59+
60+
static readonly A = "A";
61+
>A : "A"
62+
>"A" : "A"
63+
64+
static readonly B = "B";
65+
>B : "B"
66+
>"B" : "B"
67+
68+
static readonly C = "C";
69+
>C : "C"
70+
>"C" : "C"
71+
}
72+
73+
export class AKind extends ClassFactory(Kinds.A) {
74+
>AKind : AKind
75+
>ClassFactory(Kinds.A) : ClassFactory<"A">.(Anonymous class)
76+
>ClassFactory : <TKind extends string>(kind: TKind) => typeof (Anonymous class)
77+
>Kinds.A : "A"
78+
>Kinds : typeof Kinds
79+
>A : "A"
80+
}
81+
82+
export class BKind extends ClassFactory(Kinds.B) {
83+
>BKind : BKind
84+
>ClassFactory(Kinds.B) : ClassFactory<"B">.(Anonymous class)
85+
>ClassFactory : <TKind extends string>(kind: TKind) => typeof (Anonymous class)
86+
>Kinds.B : "B"
87+
>Kinds : typeof Kinds
88+
>B : "B"
89+
}
90+
91+
export class CKind extends ClassFactory(Kinds.C) {
92+
>CKind : CKind
93+
>ClassFactory(Kinds.C) : ClassFactory<"C">.(Anonymous class)
94+
>ClassFactory : <TKind extends string>(kind: TKind) => typeof (Anonymous class)
95+
>Kinds.C : "C"
96+
>Kinds : typeof Kinds
97+
>C : "C"
98+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// @declaration: true
2+
const kindCache: { [kind: string]: boolean } = {};
3+
4+
function register(kind: string): void | never {
5+
if (kindCache[kind]) {
6+
throw new Error(`Class with kind "${kind}" is already registered.`);
7+
}
8+
kindCache[kind] = true;
9+
}
10+
11+
function ClassFactory<TKind extends string>(kind: TKind) {
12+
register(kind);
13+
14+
return class {
15+
static readonly THE_KIND: TKind = kind;
16+
readonly kind: TKind = kind;
17+
};
18+
}
19+
20+
class Kinds {
21+
static readonly A = "A";
22+
static readonly B = "B";
23+
static readonly C = "C";
24+
}
25+
26+
export class AKind extends ClassFactory(Kinds.A) {
27+
}
28+
29+
export class BKind extends ClassFactory(Kinds.B) {
30+
}
31+
32+
export class CKind extends ClassFactory(Kinds.C) {
33+
}

0 commit comments

Comments
 (0)