Why Typescript interfaces allow self references?

Viewed 712
type SomeGeneric<T> = {
    x: T;
}

type TTest = SomeGeneric<TTest> & { a: string; }; // Type alias 'TTest' circularly references itself.

interface ITest extends SomeGeneric<ITest> { // OK
    a: string;
}

I fail to understand why interfaces are allowed to reference themselves in their own declarations and types aren't.

2 Answers

Type aliases can't be recursive (mostly), while interfaces can. If you search GitHub there are several answers given as to why.

For example RyanCavanaugh explains here:

Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.

Or here by DanielRosenwasser

The restriction that a type alias can't be referenced by itself at the top level has been the behavior since we implemented type aliases; however, you might recall that a while back, we started allowing type aliases to be referenced from within an object type. ... However, it has no problem provided that the type can be expanded, or "unrolled" one level at a time. In other words, as long as you have some sort of box-y thing containing the type:

The general workaround is to use an interface if you want a recursive type.

Just as a side note, the rules are not exactly enforced consistently. For example while in your case SomeGeneric<TTest> could be expanded to { x: TTest } the compiler does not even attempt it, it just fails. The following recursive definition will however work:

type TTest = { x: TTest } & { a: string; };

The reason why it works for extends is because you basically extend the interface by one more field and that is the interface itself. e.g.

private x: ITest; // is fine
private a: string;

By trying to declare a type referencing itself will result in an endless reference loop. That's why it does not work.

Related