TypeScript rejects aliases that loop back to themselves in positions other than object properties or other concrete shapes, so helper aliases or generics that indirectly name the recursive type trigger TS2456 even when the runtime structure is well-founded.
The compiler only allows a type alias to reuse itself by assigning its own structure to a property, e.g. `type Tree = { children: Tree[] }`, because that gives the checker a finite blueprint to follow. As soon as the alias appears elsewhere on the right-hand side—the alias is passed as a generic argument, unioned directly, or referenced through another alias definition—the compiler assumes it is chasing an infinite expansion and throws "Type alias 'X' circularly references itself" (TS2456). This is a deliberate restriction that keeps type resolution finite and predictable.
Start with the alias named in the error message and look at everything to the right of the equals sign. The offending pattern usually looks like this:
type Bar<A> = { x: A };
type Foo = Bar<Foo>; // TS2456Here, Foo is referenced as a generic parameter, so the compiler never sees the property-level indirection and raises the circularity error.
If possible, rewrite the alias so Foo refers to itself inside an object property, which always satisfies the recursion guard:
type Foo = {
payload: Foo;
};
// or for trees
type TreeNode = {
value: string;
children: TreeNode[];
};This keeps the self-reference visible to the checker at the property level.
When you want to keep a helper alias like Bar or reuse a field definition, convert that helper into an interface or pass a concrete property that still obeys the rule:
interface Bar<A> {
x: A;
}
type Foo = Bar<{ next: Foo }>; // Allowed because Foo only appears inside propertiesInterfaces are evaluated lazily enough that TypeScript can confirm the recursion stays inside the object structure.
TypeScript's depth-first checker may see a circularity if inherited properties are ordered differently than declared ones. In the example that raised issue #61443, moving the discriminant field before recursive children made the error go away:
type FooType = { foo?: boolean };
type BaseElement<T> = FooType & {
children: CustomElement[];
type: T;
};
type CustomElement = BaseElement<'h1'>;Flip type above children if necessary or split the intersection into named interfaces to give the checker a stable path.
After refactoring, run the compiler (or targeted file check) to ensure TS2456 disappears. If you still see the message, double-check for other aliases or intersections in the same file that might carry the circularity.
Checker short circuits on circularity. The FAQ explains that when typechecking walks through a cycle (e.g. it decides X must be assignable to Y if X is assignable to Y) it stops and emits a circularity error; the restriction about self references only inside properties is one way to keep that walk finite.
Ordering matters in complex trees. Issue #61443 shows how the same set of properties can trigger or avoid TS2456 simply by reordering discriminants versus recursive children. When TypeScript sees inherited members before explicit members, it may flag a circularity even if one does not exist.
Toolchain differences. The tsgo checker reported this bug for a tree type and required the team to store explicitly declared members ahead of inherited ones (#1968/#1987). Different TypeScript forks or versions might still emit TS2456 until those ordering heuristics are aligned.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript