Description
🔎 Search Terms
Instantiation expression, referenced directly or indirectly in its own type annotation, circular type annotation, this, parent class base constraint.
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about referenced directly or directly in its own type annotation
⏯ Playground Link
💻 Code
class WitnessIsParentClass<const out SomeClass extends ParentClass> {}
class ParentClass {
someWitness(witness: typeof WitnessIsParentClass<this>): void {}
}
class SubClass extends ParentClass {
someWitness(witness: typeof WitnessIsParentClass<this>): void { }
// ^ 'witness' is referenced directly or indirectly in its own type annotation.(2502)
}
🙁 Actual behavior
This errors with 'witness' is referenced directly or indirectly in its own type annotation.(2502)
🙂 Expected behavior
No error.
Additional information about the issue
If you change from typeof WitnessIsParentClass<this>
to WitnessIsParentClass<this>
the error goes away so this seems to be specific to instantiation expressions. I was hoping the variance annotation would give it enough information to resolve this problem but it didn't. I suppose that's probably because the variance annotations are only for the instance but not for the class as a whole.
This code example is obviously contrived but I ran into this while writing a complex mixin. I've actually ran into this style of error; 'x' is referenced directly or indirectly in its own type annotation.(2502)
many times but this was the first time it was so easy to create a minimal example. I think the root cause in those cases may have been different in those cases however, so I should probably find a way to simplify them enough to share.
Interestingly if you add a level of indirection this works fine:
class WitnessIsParentClass<SomeClass extends ParentClass> {}
type WitnessHelper<SomeClass extends ParentClass> = typeof WitnessIsParentClass<SomeClass>
class ParentClass {
someWitness(witness: WitnessHelper<this>): void {}
}
class SubClass extends ParentClass {
someWitness(witness: WitnessHelper<this>): void { }
}
I assume this is because it essentially hoists the variance check and breaks the loop or something.