Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11512,6 +11512,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return -1;
}

// Checks the entire resolution stack (including entries hidden by resolutionStart) for a symbol.
// Used to prevent false circularity errors when a contextual type lookup would attempt to resolve
// a symbol whose type is already being resolved in an outer scope.
function isSymbolTypeResolutionInProgress(symbol: Symbol): boolean {
for (let i = resolutionTargets.length - 1; i >= 0; i--) {
if (resolutionPropertyNames[i] === TypeSystemPropertyName.Type && resolutionTargets[i] === symbol) {
return true;
}
}
return false;
}

function resolutionTargetHasProperty(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean {
switch (propertyName) {
case TypeSystemPropertyName.Type:
Expand Down Expand Up @@ -32452,6 +32464,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (!prop || isCircularMappedProperty(prop)) {
return;
}
// If the property's type resolution is already in progress (possibly hidden by resolutionStart
// due to an enclosing getResolvedSignature call), avoid calling getTypeOfSymbol. Doing so would
// cause a false circularity error for static fields of class expressions passed to generic functions
// (see GH#62552).
if (isSymbolTypeResolutionInProgress(prop)) {
return;
}
return removeMissingType(getTypeOfSymbol(prop), !!(prop.flags & SymbolFlags.Optional));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//// [tests/cases/compiler/staticFieldInClassExpressionPassedToGenericFunction.ts] ////

//// [staticFieldInClassExpressionPassedToGenericFunction.ts]
// Repro from #62552: static fields in class expressions passed to generic functions
// should not incorrectly report TS7022 (implicitly has type 'any' because it does
// not have a type annotation and is referenced directly or indirectly in its own initializer)

function id<T>(x: T): T {
return x;
}

// Should not error (was incorrectly reporting TS7022 on 'foo')
const Foo = id(class {
static readonly foo = id(42);
});

// Confirm the inferred type is correct
const Foo2 = id(class {
static count = 0;
static name2 = "test";
});

// Variants with multiple static fields
const Foo3 = id(class {
static a = 1;
static b = "hello";
static c = true;
});

// No error without generic wrapper
const Ok = class {
static readonly foo = id(42);
};

// Static field referencing another static field of the same class (real circularity, should still error)
// (This is a true self-reference, not the false positive from #62552)


//// [staticFieldInClassExpressionPassedToGenericFunction.js]
"use strict";
// Repro from #62552: static fields in class expressions passed to generic functions
// should not incorrectly report TS7022 (implicitly has type 'any' because it does
// not have a type annotation and is referenced directly or indirectly in its own initializer)
function id(x) {
return x;
}
// Should not error (was incorrectly reporting TS7022 on 'foo')
const Foo = id(class {
static foo = id(42);
});
// Confirm the inferred type is correct
const Foo2 = id(class {
static count = 0;
static name2 = "test";
});
// Variants with multiple static fields
const Foo3 = id(class {
static a = 1;
static b = "hello";
static c = true;
});
// No error without generic wrapper
const Ok = class {
static foo = id(42);
};
// Static field referencing another static field of the same class (real circularity, should still error)
// (This is a true self-reference, not the false positive from #62552)
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//// [tests/cases/compiler/staticFieldInClassExpressionPassedToGenericFunction.ts] ////

=== staticFieldInClassExpressionPassedToGenericFunction.ts ===
// Repro from #62552: static fields in class expressions passed to generic functions
// should not incorrectly report TS7022 (implicitly has type 'any' because it does
// not have a type annotation and is referenced directly or indirectly in its own initializer)

function id<T>(x: T): T {
>id : Symbol(id, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 0, 0))
>T : Symbol(T, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 4, 12))
>x : Symbol(x, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 4, 15))
>T : Symbol(T, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 4, 12))
>T : Symbol(T, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 4, 12))

return x;
>x : Symbol(x, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 4, 15))
}

// Should not error (was incorrectly reporting TS7022 on 'foo')
const Foo = id(class {
>Foo : Symbol(Foo, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 9, 5))
>id : Symbol(id, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 0, 0))

static readonly foo = id(42);
>foo : Symbol((Anonymous class).foo, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 9, 22))
>id : Symbol(id, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 0, 0))

});

// Confirm the inferred type is correct
const Foo2 = id(class {
>Foo2 : Symbol(Foo2, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 14, 5))
>id : Symbol(id, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 0, 0))

static count = 0;
>count : Symbol((Anonymous class).count, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 14, 23))

static name2 = "test";
>name2 : Symbol((Anonymous class).name2, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 15, 21))

});

// Variants with multiple static fields
const Foo3 = id(class {
>Foo3 : Symbol(Foo3, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 20, 5))
>id : Symbol(id, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 0, 0))

static a = 1;
>a : Symbol((Anonymous class).a, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 20, 23))

static b = "hello";
>b : Symbol((Anonymous class).b, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 21, 17))

static c = true;
>c : Symbol((Anonymous class).c, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 22, 23))

});

// No error without generic wrapper
const Ok = class {
>Ok : Symbol(Ok, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 27, 5))

static readonly foo = id(42);
>foo : Symbol(Ok.foo, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 27, 18))
>id : Symbol(id, Decl(staticFieldInClassExpressionPassedToGenericFunction.ts, 0, 0))

};

// Static field referencing another static field of the same class (real circularity, should still error)
// (This is a true self-reference, not the false positive from #62552)

Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//// [tests/cases/compiler/staticFieldInClassExpressionPassedToGenericFunction.ts] ////

=== staticFieldInClassExpressionPassedToGenericFunction.ts ===
// Repro from #62552: static fields in class expressions passed to generic functions
// should not incorrectly report TS7022 (implicitly has type 'any' because it does
// not have a type annotation and is referenced directly or indirectly in its own initializer)

function id<T>(x: T): T {
>id : <T>(x: T) => T
> : ^ ^^ ^^ ^^^^^
>x : T
> : ^

return x;
>x : T
> : ^
}

// Should not error (was incorrectly reporting TS7022 on 'foo')
const Foo = id(class {
>Foo : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>id(class { static readonly foo = id(42);}) : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>id : <T>(x: T) => T
> : ^ ^^ ^^ ^^^^^
>class { static readonly foo = id(42);} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^

static readonly foo = id(42);
>foo : 42
> : ^^
>id(42) : 42
> : ^^
>id : <T>(x: T) => T
> : ^ ^^ ^^ ^^^^^
>42 : 42
> : ^^

});

// Confirm the inferred type is correct
const Foo2 = id(class {
>Foo2 : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>id(class { static count = 0; static name2 = "test";}) : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>id : <T>(x: T) => T
> : ^ ^^ ^^ ^^^^^
>class { static count = 0; static name2 = "test";} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^

static count = 0;
>count : number
> : ^^^^^^
>0 : 0
> : ^

static name2 = "test";
>name2 : string
> : ^^^^^^
>"test" : "test"
> : ^^^^^^

});

// Variants with multiple static fields
const Foo3 = id(class {
>Foo3 : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>id(class { static a = 1; static b = "hello"; static c = true;}) : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>id : <T>(x: T) => T
> : ^ ^^ ^^ ^^^^^
>class { static a = 1; static b = "hello"; static c = true;} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^

static a = 1;
>a : number
> : ^^^^^^
>1 : 1
> : ^

static b = "hello";
>b : string
> : ^^^^^^
>"hello" : "hello"
> : ^^^^^^^

static c = true;
>c : boolean
> : ^^^^^^^
>true : true
> : ^^^^

});

// No error without generic wrapper
const Ok = class {
>Ok : typeof Ok
> : ^^^^^^^^^
>class { static readonly foo = id(42);} : typeof Ok
> : ^^^^^^^^^

static readonly foo = id(42);
>foo : 42
> : ^^
>id(42) : 42
> : ^^
>id : <T>(x: T) => T
> : ^ ^^ ^^ ^^^^^
>42 : 42
> : ^^

};

// Static field referencing another static field of the same class (real circularity, should still error)
// (This is a true self-reference, not the false positive from #62552)

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// @noImplicitAny: true

// Repro from #62552: static fields in class expressions passed to generic functions
// should not incorrectly report TS7022 (implicitly has type 'any' because it does
// not have a type annotation and is referenced directly or indirectly in its own initializer)

function id<T>(x: T): T {
return x;
}

// Should not error (was incorrectly reporting TS7022 on 'foo')
const Foo = id(class {
static readonly foo = id(42);
});

// Confirm the inferred type is correct
const Foo2 = id(class {
static count = 0;
static name2 = "test";
});

// Variants with multiple static fields
const Foo3 = id(class {
static a = 1;
static b = "hello";
static c = true;
});

// No error without generic wrapper
const Ok = class {
static readonly foo = id(42);
};

// Static field referencing another static field of the same class (real circularity, should still error)
// (This is a true self-reference, not the false positive from #62552)