Allow forward references in computed properties of type declarations (#50824)

* Allow forward references in type declarations

* address PR feedback
This commit is contained in:
Mateusz Burzyński 2022-12-30 20:37:33 +01:00 committed by GitHub
parent 79244c578f
commit e9cd2e14db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 452 additions and 80 deletions

View File

@ -2709,7 +2709,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return true;
}
if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || usageInTypeDeclaration()) {
if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isInAmbientOrTypeNode(usage)) {
return true;
}
if (isUsedInFunctionOrInstanceProperty(usage, declaration)) {
@ -2724,10 +2724,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
return false;
function usageInTypeDeclaration() {
return !!findAncestor(usage, node => isInterfaceDeclaration(node) || isTypeAliasDeclaration(node));
}
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
switch (declaration.parent.parent.kind) {
case SyntaxKind.VariableStatement:
@ -24620,6 +24616,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
n => n.kind === SyntaxKind.TypeQuery ? true : n.kind === SyntaxKind.Identifier || n.kind === SyntaxKind.QualifiedName ? false : "quit");
}
function isInAmbientOrTypeNode(node: Node): boolean {
return !!(node.flags & NodeFlags.Ambient || findAncestor(node, n => isInterfaceDeclaration(n) || isTypeLiteralNode(n)));
}
// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers
// separated by dots). The key consists of the id of the symbol referenced by the
// leftmost identifier followed by zero or more property names separated by dots.
@ -27293,7 +27293,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// declaration container are the same).
const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) ||
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 ||
isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
isInTypeQuery(node) || isInAmbientOrTypeNode(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
node.parent.kind === SyntaxKind.NonNullExpression ||
declaration.kind === SyntaxKind.VariableDeclaration && (declaration as VariableDeclaration).exclamationToken ||
declaration.flags & NodeFlags.Ambient;

View File

@ -0,0 +1,42 @@
//// [forwardRefInTypeDeclaration.ts]
// forward ref ignored in a typeof
declare let s: typeof s1;
const s1 = "x";
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
const s2 = "x";
// or in a type definition
type Foo3 = { [s3]: number; }
const s3 = "x";
// or in a type literal
declare const foo4: { [s4]: number; }
const s4 = "x";
// or in a declared class
declare class Foo5 { [s5]: number; }
const s5 = "x";
// or with qualified names
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
declare class Cls1 { static a: "a"; }
class Cls2 { static b = "b" as const; }
declare const obj1: { c: 'c' }
const obj2 = { d: 'd' } as const
//// [forwardRefInTypeDeclaration.js]
var s1 = "x";
var s2 = "x";
var s3 = "x";
var s4 = "x";
var s5 = "x";
var Cls2 = /** @class */ (function () {
function Cls2() {
}
Cls2.b = "b";
return Cls2;
}());
var obj2 = { d: 'd' };

View File

@ -0,0 +1,83 @@
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
// forward ref ignored in a typeof
declare let s: typeof s1;
>s : Symbol(s, Decl(forwardRefInTypeDeclaration.ts, 1, 11))
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
const s1 = "x";
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
>Foo2 : Symbol(Foo2, Decl(forwardRefInTypeDeclaration.ts, 2, 15))
>[s2] : Symbol(Foo2[s2], Decl(forwardRefInTypeDeclaration.ts, 5, 16))
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
const s2 = "x";
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
// or in a type definition
type Foo3 = { [s3]: number; }
>Foo3 : Symbol(Foo3, Decl(forwardRefInTypeDeclaration.ts, 6, 15))
>[s3] : Symbol([s3], Decl(forwardRefInTypeDeclaration.ts, 9, 13))
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
const s3 = "x";
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
// or in a type literal
declare const foo4: { [s4]: number; }
>foo4 : Symbol(foo4, Decl(forwardRefInTypeDeclaration.ts, 13, 13))
>[s4] : Symbol([s4], Decl(forwardRefInTypeDeclaration.ts, 13, 21))
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
const s4 = "x";
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
// or in a declared class
declare class Foo5 { [s5]: number; }
>Foo5 : Symbol(Foo5, Decl(forwardRefInTypeDeclaration.ts, 14, 15))
>[s5] : Symbol(Foo5[s5], Decl(forwardRefInTypeDeclaration.ts, 17, 20))
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
const s5 = "x";
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
// or with qualified names
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
>Foo6 : Symbol(Foo6, Decl(forwardRefInTypeDeclaration.ts, 18, 15))
>[Cls1.a] : Symbol(Foo6[Cls1.a], Decl(forwardRefInTypeDeclaration.ts, 21, 16))
>Cls1.a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
>[Cls2.b] : Symbol(Foo6[Cls2.b], Decl(forwardRefInTypeDeclaration.ts, 21, 34))
>Cls2.b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
>[obj1.c] : Symbol(Foo6[obj1.c], Decl(forwardRefInTypeDeclaration.ts, 21, 52))
>obj1.c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
>[obj2.d] : Symbol(Foo6[obj2.d], Decl(forwardRefInTypeDeclaration.ts, 21, 70))
>obj2.d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
declare class Cls1 { static a: "a"; }
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
class Cls2 { static b = "b" as const; }
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
>const : Symbol(const)
declare const obj1: { c: 'c' }
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
const obj2 = { d: 'd' } as const
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
>const : Symbol(const)

View File

@ -0,0 +1,89 @@
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
// forward ref ignored in a typeof
declare let s: typeof s1;
>s : "x"
>s1 : "x"
const s1 = "x";
>s1 : "x"
>"x" : "x"
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
>[s2] : number
>s2 : "x"
const s2 = "x";
>s2 : "x"
>"x" : "x"
// or in a type definition
type Foo3 = { [s3]: number; }
>Foo3 : { x: number; }
>[s3] : number
>s3 : "x"
const s3 = "x";
>s3 : "x"
>"x" : "x"
// or in a type literal
declare const foo4: { [s4]: number; }
>foo4 : { x: number; }
>[s4] : number
>s4 : "x"
const s4 = "x";
>s4 : "x"
>"x" : "x"
// or in a declared class
declare class Foo5 { [s5]: number; }
>Foo5 : Foo5
>[s5] : number
>s5 : "x"
const s5 = "x";
>s5 : "x"
>"x" : "x"
// or with qualified names
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
>[Cls1.a] : number
>Cls1.a : "a"
>Cls1 : typeof Cls1
>a : "a"
>[Cls2.b] : number
>Cls2.b : "b"
>Cls2 : typeof Cls2
>b : "b"
>[obj1.c] : number
>obj1.c : "c"
>obj1 : { c: "c"; }
>c : "c"
>[obj2.d] : number
>obj2.d : "d"
>obj2 : { readonly d: "d"; }
>d : "d"
declare class Cls1 { static a: "a"; }
>Cls1 : Cls1
>a : "a"
class Cls2 { static b = "b" as const; }
>Cls2 : Cls2
>b : "b"
>"b" as const : "b"
>"b" : "b"
declare const obj1: { c: 'c' }
>obj1 : { c: 'c'; }
>c : "c"
const obj2 = { d: 'd' } as const
>obj2 : { readonly d: "d"; }
>{ d: 'd' } as const : { readonly d: "d"; }
>{ d: 'd' } : { readonly d: "d"; }
>d : "d"
>'d' : "d"

View File

@ -0,0 +1,43 @@
//// [forwardRefInTypeDeclaration.ts]
// forward ref ignored in a typeof
declare let s: typeof s1;
const s1 = "x";
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
const s2 = "x";
// or in a type definition
type Foo3 = { [s3]: number; }
const s3 = "x";
// or in a type literal
declare const foo4: { [s4]: number; }
const s4 = "x";
// or in a declared class
declare class Foo5 { [s5]: number; }
const s5 = "x";
// or with qualified names
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
declare class Cls1 { static a: "a"; }
class Cls2 { static b = "b" as const; }
declare const obj1: { c: 'c' }
const obj2 = { d: 'd' } as const
//// [forwardRefInTypeDeclaration.js]
"use strict";
var s1 = "x";
var s2 = "x";
var s3 = "x";
var s4 = "x";
var s5 = "x";
var Cls2 = /** @class */ (function () {
function Cls2() {
}
Cls2.b = "b";
return Cls2;
}());
var obj2 = { d: 'd' };

View File

@ -0,0 +1,83 @@
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
// forward ref ignored in a typeof
declare let s: typeof s1;
>s : Symbol(s, Decl(forwardRefInTypeDeclaration.ts, 1, 11))
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
const s1 = "x";
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
>Foo2 : Symbol(Foo2, Decl(forwardRefInTypeDeclaration.ts, 2, 15))
>[s2] : Symbol(Foo2[s2], Decl(forwardRefInTypeDeclaration.ts, 5, 16))
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
const s2 = "x";
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
// or in a type definition
type Foo3 = { [s3]: number; }
>Foo3 : Symbol(Foo3, Decl(forwardRefInTypeDeclaration.ts, 6, 15))
>[s3] : Symbol([s3], Decl(forwardRefInTypeDeclaration.ts, 9, 13))
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
const s3 = "x";
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
// or in a type literal
declare const foo4: { [s4]: number; }
>foo4 : Symbol(foo4, Decl(forwardRefInTypeDeclaration.ts, 13, 13))
>[s4] : Symbol([s4], Decl(forwardRefInTypeDeclaration.ts, 13, 21))
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
const s4 = "x";
>s4 : Symbol(s4, Decl(forwardRefInTypeDeclaration.ts, 14, 5))
// or in a declared class
declare class Foo5 { [s5]: number; }
>Foo5 : Symbol(Foo5, Decl(forwardRefInTypeDeclaration.ts, 14, 15))
>[s5] : Symbol(Foo5[s5], Decl(forwardRefInTypeDeclaration.ts, 17, 20))
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
const s5 = "x";
>s5 : Symbol(s5, Decl(forwardRefInTypeDeclaration.ts, 18, 5))
// or with qualified names
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
>Foo6 : Symbol(Foo6, Decl(forwardRefInTypeDeclaration.ts, 18, 15))
>[Cls1.a] : Symbol(Foo6[Cls1.a], Decl(forwardRefInTypeDeclaration.ts, 21, 16))
>Cls1.a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
>[Cls2.b] : Symbol(Foo6[Cls2.b], Decl(forwardRefInTypeDeclaration.ts, 21, 34))
>Cls2.b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
>[obj1.c] : Symbol(Foo6[obj1.c], Decl(forwardRefInTypeDeclaration.ts, 21, 52))
>obj1.c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
>[obj2.d] : Symbol(Foo6[obj2.d], Decl(forwardRefInTypeDeclaration.ts, 21, 70))
>obj2.d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
declare class Cls1 { static a: "a"; }
>Cls1 : Symbol(Cls1, Decl(forwardRefInTypeDeclaration.ts, 21, 89))
>a : Symbol(Cls1.a, Decl(forwardRefInTypeDeclaration.ts, 22, 20))
class Cls2 { static b = "b" as const; }
>Cls2 : Symbol(Cls2, Decl(forwardRefInTypeDeclaration.ts, 22, 37))
>b : Symbol(Cls2.b, Decl(forwardRefInTypeDeclaration.ts, 23, 12))
>const : Symbol(const)
declare const obj1: { c: 'c' }
>obj1 : Symbol(obj1, Decl(forwardRefInTypeDeclaration.ts, 24, 13))
>c : Symbol(c, Decl(forwardRefInTypeDeclaration.ts, 24, 21))
const obj2 = { d: 'd' } as const
>obj2 : Symbol(obj2, Decl(forwardRefInTypeDeclaration.ts, 25, 5))
>d : Symbol(d, Decl(forwardRefInTypeDeclaration.ts, 25, 14))
>const : Symbol(const)

View File

@ -0,0 +1,89 @@
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
// forward ref ignored in a typeof
declare let s: typeof s1;
>s : "x"
>s1 : "x"
const s1 = "x";
>s1 : "x"
>"x" : "x"
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
>[s2] : number
>s2 : "x"
const s2 = "x";
>s2 : "x"
>"x" : "x"
// or in a type definition
type Foo3 = { [s3]: number; }
>Foo3 : { x: number; }
>[s3] : number
>s3 : "x"
const s3 = "x";
>s3 : "x"
>"x" : "x"
// or in a type literal
declare const foo4: { [s4]: number; }
>foo4 : { x: number; }
>[s4] : number
>s4 : "x"
const s4 = "x";
>s4 : "x"
>"x" : "x"
// or in a declared class
declare class Foo5 { [s5]: number; }
>Foo5 : Foo5
>[s5] : number
>s5 : "x"
const s5 = "x";
>s5 : "x"
>"x" : "x"
// or with qualified names
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
>[Cls1.a] : number
>Cls1.a : "a"
>Cls1 : typeof Cls1
>a : "a"
>[Cls2.b] : number
>Cls2.b : "b"
>Cls2 : typeof Cls2
>b : "b"
>[obj1.c] : number
>obj1.c : "c"
>obj1 : { c: "c"; }
>c : "c"
>[obj2.d] : number
>obj2.d : "d"
>obj2 : { readonly d: "d"; }
>d : "d"
declare class Cls1 { static a: "a"; }
>Cls1 : Cls1
>a : "a"
class Cls2 { static b = "b" as const; }
>Cls2 : Cls2
>b : "b"
>"b" as const : "b"
>"b" : "b"
declare const obj1: { c: 'c' }
>obj1 : { c: 'c'; }
>c : "c"
const obj2 = { d: 'd' } as const
>obj2 : { readonly d: "d"; }
>{ d: 'd' } as const : { readonly d: "d"; }
>{ d: 'd' } : { readonly d: "d"; }
>d : "d"
>'d' : "d"

View File

@ -1,18 +0,0 @@
//// [forwardRefInTypeDeclaration.ts]
// forward ref ignored in a typeof
declare let s: typeof s1;
const s1 = "x";
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
const s2 = "x";
// or in a type definition
type Foo3 = { [s3]: number; }
const s3 = "x";
//// [forwardRefInTypeDeclaration.js]
var s1 = "x";
var s2 = "x";
var s3 = "x";

View File

@ -1,27 +0,0 @@
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
// forward ref ignored in a typeof
declare let s: typeof s1;
>s : Symbol(s, Decl(forwardRefInTypeDeclaration.ts, 1, 11))
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
const s1 = "x";
>s1 : Symbol(s1, Decl(forwardRefInTypeDeclaration.ts, 2, 5))
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
>Foo2 : Symbol(Foo2, Decl(forwardRefInTypeDeclaration.ts, 2, 15))
>[s2] : Symbol(Foo2[s2], Decl(forwardRefInTypeDeclaration.ts, 5, 16))
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
const s2 = "x";
>s2 : Symbol(s2, Decl(forwardRefInTypeDeclaration.ts, 6, 5))
// or in a type definition
type Foo3 = { [s3]: number; }
>Foo3 : Symbol(Foo3, Decl(forwardRefInTypeDeclaration.ts, 6, 15))
>[s3] : Symbol([s3], Decl(forwardRefInTypeDeclaration.ts, 9, 13))
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))
const s3 = "x";
>s3 : Symbol(s3, Decl(forwardRefInTypeDeclaration.ts, 10, 5))

View File

@ -1,29 +0,0 @@
=== tests/cases/compiler/forwardRefInTypeDeclaration.ts ===
// forward ref ignored in a typeof
declare let s: typeof s1;
>s : "x"
>s1 : "x"
const s1 = "x";
>s1 : "x"
>"x" : "x"
// ignored anywhere in an interface (#35947)
interface Foo2 { [s2]: number; }
>[s2] : number
>s2 : "x"
const s2 = "x";
>s2 : "x"
>"x" : "x"
// or in a type definition
type Foo3 = { [s3]: number; }
>Foo3 : { x: number; }
>[s3] : number
>s3 : "x"
const s3 = "x";
>s3 : "x"
>"x" : "x"

View File

@ -1,3 +1,5 @@
// @strict: true,false
// forward ref ignored in a typeof
declare let s: typeof s1;
const s1 = "x";
@ -9,3 +11,18 @@ const s2 = "x";
// or in a type definition
type Foo3 = { [s3]: number; }
const s3 = "x";
// or in a type literal
declare const foo4: { [s4]: number; }
const s4 = "x";
// or in a declared class
declare class Foo5 { [s5]: number; }
const s5 = "x";
// or with qualified names
interface Foo6 { [Cls1.a]: number; [Cls2.b]: number; [obj1.c]: number; [obj2.d]: number }
declare class Cls1 { static a: "a"; }
class Cls2 { static b = "b" as const; }
declare const obj1: { c: 'c' }
const obj2 = { d: 'd' } as const