Skip to content

Commit e9cd2e1

Browse files
authored
Allow forward references in computed properties of type declarations (#50824)
* Allow forward references in type declarations * address PR feedback
1 parent 79244c5 commit e9cd2e1

11 files changed

+452
-80
lines changed

src/compiler/checker.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -2709,7 +2709,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
27092709
return true;
27102710
}
27112711

2712-
if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || usageInTypeDeclaration()) {
2712+
if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isInAmbientOrTypeNode(usage)) {
27132713
return true;
27142714
}
27152715
if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
@@ -2724,10 +2724,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
27242724
}
27252725
return false;
27262726

2727-
function usageInTypeDeclaration() {
2728-
return !!findAncestor(usage, node => isInterfaceDeclaration(node) || isTypeAliasDeclaration(node));
2729-
}
2730-
27312727
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
27322728
switch (declaration.parent.parent.kind) {
27332729
case SyntaxKind.VariableStatement:
@@ -24620,6 +24616,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2462024616
n => n.kind === SyntaxKind.TypeQuery ? true : n.kind === SyntaxKind.Identifier || n.kind === SyntaxKind.QualifiedName ? false : "quit");
2462124617
}
2462224618

24619+
function isInAmbientOrTypeNode(node: Node): boolean {
24620+
return !!(node.flags & NodeFlags.Ambient || findAncestor(node, n => isInterfaceDeclaration(n) || isTypeLiteralNode(n)));
24621+
}
24622+
2462324623
// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers
2462424624
// separated by dots). The key consists of the id of the symbol referenced by the
2462524625
// leftmost identifier followed by zero or more property names separated by dots.
@@ -27293,7 +27293,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2729327293
// declaration container are the same).
2729427294
const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) ||
2729527295
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 ||
27296-
isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
27296+
isInTypeQuery(node) || isInAmbientOrTypeNode(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
2729727297
node.parent.kind === SyntaxKind.NonNullExpression ||
2729827298
declaration.kind === SyntaxKind.VariableDeclaration && (declaration as VariableDeclaration).exclamationToken ||
2729927299
declaration.flags & NodeFlags.Ambient;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [forwardRefInTypeDeclaration.ts]
2+
// forward ref ignored in a typeof
3+
declare let s: typeof s1;
4+
const s1 = "x";
5+
6+
// ignored anywhere in an interface (#35947)
7+
interface Foo2 { [s2]: number; }
8+
const s2 = "x";
9+
10+
// or in a type definition
11+
type Foo3 = { [s3]: number; }
12+
const s3 = "x";
13+
14+
// or in a type literal
15+
declare const foo4: { [s4]: number; }
16+
const s4 = "x";
17+
18+
// or in a declared class
19+
declare class Foo5 { [s5]: number; }
20+
const s5 = "x";
21+
22+
// or with qualified names
23+
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
24+
declare class Cls1 { static a: "a"; }
25+
class Cls2 { static b = "b" as const; }
26+
declare const obj1: { c: 'c' }
27+
const obj2 = { d: 'd' } as const
28+
29+
30+
//// [forwardRefInTypeDeclaration.js]
31+
var s1 = "x";
32+
var s2 = "x";
33+
var s3 = "x";
34+
var s4 = "x";
35+
var s5 = "x";
36+
var Cls2 = /** @class */ (function () {
37+
function Cls2() {
38+
}
39+
Cls2.b = "b";
40+
return Cls2;
41+
}());
42+
var obj2 = { d: 'd' };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
2+
// forward ref ignored in a typeof
3+
declare let s: typeof s1;
4+
>s : Symbol(s, Decl(forwardRefInTypeDeclaration.ts, 1, 11))
5+
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
6+
7+
const s1 = "x";
8+
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
9+
10+
// ignored anywhere in an interface (#35947)
11+
interface Foo2 { [s2]: number; }
12+
>Foo2 : Symbol(Foo2, Decl(forwardRefInTypeDeclaration.ts, 2, 15))
13+
>[s2] : Symbol(Foo2[s2], Decl(forwardRefInTypeDeclaration.ts, 5, 16))
14+
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
15+
16+
const s2 = "x";
17+
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
18+
19+
// or in a type definition
20+
type Foo3 = { [s3]: number; }
21+
>Foo3 : Symbol(Foo3, Decl(forwardRefInTypeDeclaration.ts, 6, 15))
22+
>[s3] : Symbol([s3], Decl(forwardRefInTypeDeclaration.ts, 9, 13))
23+
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
24+
25+
const s3 = "x";
26+
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
27+
28+
// or in a type literal
29+
declare const foo4: { [s4]: number; }
30+
>foo4 : Symbol(foo4, Decl(forwardRefInTypeDeclaration.ts, 13, 13))
31+
>[s4] : Symbol([s4], Decl(forwardRefInTypeDeclaration.ts, 13, 21))
32+
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
33+
34+
const s4 = "x";
35+
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
36+
37+
// or in a declared class
38+
declare class Foo5 { [s5]: number; }
39+
>Foo5 : Symbol(Foo5, Decl(forwardRefInTypeDeclaration.ts, 14, 15))
40+
>[s5] : Symbol(Foo5[s5], Decl(forwardRefInTypeDeclaration.ts, 17, 20))
41+
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
42+
43+
const s5 = "x";
44+
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
45+
46+
// or with qualified names
47+
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
48+
>Foo6 : Symbol(Foo6, Decl(forwardRefInTypeDeclaration.ts, 18, 15))
49+
>[Cls1.a] : Symbol(Foo6[Cls1.a], Decl(forwardRefInTypeDeclaration.ts, 21, 16))
50+
>Cls1.a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
51+
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
52+
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
53+
>[Cls2.b] : Symbol(Foo6[Cls2.b], Decl(forwardRefInTypeDeclaration.ts, 21, 34))
54+
>Cls2.b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
55+
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
56+
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
57+
>[obj1.c] : Symbol(Foo6[obj1.c], Decl(forwardRefInTypeDeclaration.ts, 21, 52))
58+
>obj1.c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
59+
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
60+
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
61+
>[obj2.d] : Symbol(Foo6[obj2.d], Decl(forwardRefInTypeDeclaration.ts, 21, 70))
62+
>obj2.d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
63+
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
64+
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
65+
66+
declare class Cls1 { static a: "a"; }
67+
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
68+
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
69+
70+
class Cls2 { static b = "b" as const; }
71+
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
72+
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
73+
>const : Symbol(const)
74+
75+
declare const obj1: { c: 'c' }
76+
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
77+
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
78+
79+
const obj2 = { d: 'd' } as const
80+
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
81+
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
82+
>const : Symbol(const)
83+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
2+
// forward ref ignored in a typeof
3+
declare let s: typeof s1;
4+
>s : "x"
5+
>s1 : "x"
6+
7+
const s1 = "x";
8+
>s1 : "x"
9+
>"x" : "x"
10+
11+
// ignored anywhere in an interface (#35947)
12+
interface Foo2 { [s2]: number; }
13+
>[s2] : number
14+
>s2 : "x"
15+
16+
const s2 = "x";
17+
>s2 : "x"
18+
>"x" : "x"
19+
20+
// or in a type definition
21+
type Foo3 = { [s3]: number; }
22+
>Foo3 : { x: number; }
23+
>[s3] : number
24+
>s3 : "x"
25+
26+
const s3 = "x";
27+
>s3 : "x"
28+
>"x" : "x"
29+
30+
// or in a type literal
31+
declare const foo4: { [s4]: number; }
32+
>foo4 : { x: number; }
33+
>[s4] : number
34+
>s4 : "x"
35+
36+
const s4 = "x";
37+
>s4 : "x"
38+
>"x" : "x"
39+
40+
// or in a declared class
41+
declare class Foo5 { [s5]: number; }
42+
>Foo5 : Foo5
43+
>[s5] : number
44+
>s5 : "x"
45+
46+
const s5 = "x";
47+
>s5 : "x"
48+
>"x" : "x"
49+
50+
// or with qualified names
51+
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
52+
>[Cls1.a] : number
53+
>Cls1.a : "a"
54+
>Cls1 : typeof Cls1
55+
>a : "a"
56+
>[Cls2.b] : number
57+
>Cls2.b : "b"
58+
>Cls2 : typeof Cls2
59+
>b : "b"
60+
>[obj1.c] : number
61+
>obj1.c : "c"
62+
>obj1 : { c: "c"; }
63+
>c : "c"
64+
>[obj2.d] : number
65+
>obj2.d : "d"
66+
>obj2 : { readonly d: "d"; }
67+
>d : "d"
68+
69+
declare class Cls1 { static a: "a"; }
70+
>Cls1 : Cls1
71+
>a : "a"
72+
73+
class Cls2 { static b = "b" as const; }
74+
>Cls2 : Cls2
75+
>b : "b"
76+
>"b" as const : "b"
77+
>"b" : "b"
78+
79+
declare const obj1: { c: 'c' }
80+
>obj1 : { c: 'c'; }
81+
>c : "c"
82+
83+
const obj2 = { d: 'd' } as const
84+
>obj2 : { readonly d: "d"; }
85+
>{ d: 'd' } as const : { readonly d: "d"; }
86+
>{ d: 'd' } : { readonly d: "d"; }
87+
>d : "d"
88+
>'d' : "d"
89+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [forwardRefInTypeDeclaration.ts]
2+
// forward ref ignored in a typeof
3+
declare let s: typeof s1;
4+
const s1 = "x";
5+
6+
// ignored anywhere in an interface (#35947)
7+
interface Foo2 { [s2]: number; }
8+
const s2 = "x";
9+
10+
// or in a type definition
11+
type Foo3 = { [s3]: number; }
12+
const s3 = "x";
13+
14+
// or in a type literal
15+
declare const foo4: { [s4]: number; }
16+
const s4 = "x";
17+
18+
// or in a declared class
19+
declare class Foo5 { [s5]: number; }
20+
const s5 = "x";
21+
22+
// or with qualified names
23+
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
24+
declare class Cls1 { static a: "a"; }
25+
class Cls2 { static b = "b" as const; }
26+
declare const obj1: { c: 'c' }
27+
const obj2 = { d: 'd' } as const
28+
29+
30+
//// [forwardRefInTypeDeclaration.js]
31+
"use strict";
32+
var s1 = "x";
33+
var s2 = "x";
34+
var s3 = "x";
35+
var s4 = "x";
36+
var s5 = "x";
37+
var Cls2 = /** @class */ (function () {
38+
function Cls2() {
39+
}
40+
Cls2.b = "b";
41+
return Cls2;
42+
}());
43+
var obj2 = { d: 'd' };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
2+
// forward ref ignored in a typeof
3+
declare let s: typeof s1;
4+
>s : Symbol(s, Decl(forwardRefInTypeDeclaration.ts, 1, 11))
5+
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
6+
7+
const s1 = "x";
8+
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
9+
10+
// ignored anywhere in an interface (#35947)
11+
interface Foo2 { [s2]: number; }
12+
>Foo2 : Symbol(Foo2, Decl(forwardRefInTypeDeclaration.ts, 2, 15))
13+
>[s2] : Symbol(Foo2[s2], Decl(forwardRefInTypeDeclaration.ts, 5, 16))
14+
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
15+
16+
const s2 = "x";
17+
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
18+
19+
// or in a type definition
20+
type Foo3 = { [s3]: number; }
21+
>Foo3 : Symbol(Foo3, Decl(forwardRefInTypeDeclaration.ts, 6, 15))
22+
>[s3] : Symbol([s3], Decl(forwardRefInTypeDeclaration.ts, 9, 13))
23+
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
24+
25+
const s3 = "x";
26+
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
27+
28+
// or in a type literal
29+
declare const foo4: { [s4]: number; }
30+
>foo4 : Symbol(foo4, Decl(forwardRefInTypeDeclaration.ts, 13, 13))
31+
>[s4] : Symbol([s4], Decl(forwardRefInTypeDeclaration.ts, 13, 21))
32+
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
33+
34+
const s4 = "x";
35+
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
36+
37+
// or in a declared class
38+
declare class Foo5 { [s5]: number; }
39+
>Foo5 : Symbol(Foo5, Decl(forwardRefInTypeDeclaration.ts, 14, 15))
40+
>[s5] : Symbol(Foo5[s5], Decl(forwardRefInTypeDeclaration.ts, 17, 20))
41+
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
42+
43+
const s5 = "x";
44+
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
45+
46+
// or with qualified names
47+
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
48+
>Foo6 : Symbol(Foo6, Decl(forwardRefInTypeDeclaration.ts, 18, 15))
49+
>[Cls1.a] : Symbol(Foo6[Cls1.a], Decl(forwardRefInTypeDeclaration.ts, 21, 16))
50+
>Cls1.a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
51+
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
52+
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
53+
>[Cls2.b] : Symbol(Foo6[Cls2.b], Decl(forwardRefInTypeDeclaration.ts, 21, 34))
54+
>Cls2.b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
55+
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
56+
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
57+
>[obj1.c] : Symbol(Foo6[obj1.c], Decl(forwardRefInTypeDeclaration.ts, 21, 52))
58+
>obj1.c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
59+
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
60+
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
61+
>[obj2.d] : Symbol(Foo6[obj2.d], Decl(forwardRefInTypeDeclaration.ts, 21, 70))
62+
>obj2.d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
63+
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
64+
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
65+
66+
declare class Cls1 { static a: "a"; }
67+
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
68+
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
69+
70+
class Cls2 { static b = "b" as const; }
71+
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
72+
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
73+
>const : Symbol(const)
74+
75+
declare const obj1: { c: 'c' }
76+
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
77+
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
78+
79+
const obj2 = { d: 'd' } as const
80+
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
81+
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
82+
>const : Symbol(const)
83+

0 commit comments

Comments
 (0)