Microsoft/TypeScript

Mixing base class defeats circularity protection

Open

#61,721 opened on May 18, 2025

View on GitHub
ย (0 comments)ย (0 reactions)ย (0 assignees)TypeScriptย (48,455 stars)ย (6,726 forks)batch import
Domain: check: Type CircularityHelp WantedPossible Improvement

Description

๐Ÿ”Ž Search Terms

mixin, referenced directly or indirectly in its own type annotation, recursively references itself as a base type, referenced directly or indirectly in its own base expression.

๐Ÿ•— Version & Regression Information

  • This changed in the PR #39675

Given that this was when base types were consistently checked for errors this has effectively existed forever, just only started being reported in #39675.

โฏ Playground Link

https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEkoGdnwEIpAYQPYFsAjKAFygDsT4BvAKHkV3ORJgFcwTcYAKAOgGwA5sgBc8ANp4ipCiQkAiAA4xcShQF0NASgDc9eAHpDDU2fNmAetZu271+AHJhyR-ACWaOADMQccmAgwPDA7nCcEACe8Nwe5KHhJFFxHiRouADu5PAkkUoIFOS4ZCTuTHy0AL60tKBIcIjQqPAAkpR+5FAQ2BDuIJQAIrhgbPj9VHQMYEws7Jzc-IIwIuIUkRI6+tW14NAN3mwBpUzwPX2Dw6PjALLuAB7u5AA8mMg4TcgAfDwGryDifx6KGQtG04ly+Vw3la7RgnW6vXGQxGY0o8AAZBgsEDUPpjPBLPAAEogfC4ABuCAABhCQFCYSQOl0zkjLqiSFT4BRgiQABYIMBhEZ7dy5eBCXAgNBQDJQSIVHb1AUfU4EYhkNEgO6M+JoFkXFE3e6PHiAtWySjaGgGfEWcz2QkAFTyCEc0nVcjc4TYMGQ7kpyR8fn6gTQoreEGhKC58GIbxyLoqDFtdtMDqc7otJDcnngQf8gWCCXASWisUexYi0UeqXSWVjWHgWpUUr95QMKjU4nIo0Ifi2NSAA

๐Ÿ’ป Code

declare class BaseCombatant {
  constructor(...args: [Combatant["prop"]]);
  //                  ^^^^^^^^^^^^^^^^^ 'args' is referenced directly or indirectly in its own type annotation.
}

declare class InternalClientDocument {
  constructor(...args: any[]);
}

declare function ClientDocumentMixin<BaseClass>(
  Base: BaseClass
): typeof InternalClientDocument & BaseClass;
// ^ Remove `typeof InternalClientDocument` and the circularity goes away.

declare class Combatant extends ClientDocumentMixin(BaseCombatant) {
  //            ^^^^^^^ Type 'Combatant' recursively references itself as a base type.
  //            ^^^^^^^ 'Combatant' is referenced directly or indirectly in its own base expression.
  prop: number;
}

๐Ÿ™ Actual behavior

Numerous errors.

๐Ÿ™‚ Expected behavior

No error. This pattern is not intractable, in fact if you remove the useless typeof InternalClientDocument in typeof InternalClientDocument & BaseClass it works.

In reality I'm actually dealing with a useful mixin, not an empty class, so I would prefer this to work for a complete mixin as well. I'm guessing the issue has something to do with weaker caching around arbitrary expressions as the base class compared to the typical base class with a concrete name.

Additional information about the issue

No response

Contributor guide

Mixing base class defeats circularity protection ยท Microsoft/TypeScript#61721 | Good First Issue