Kindzoku
Kindzoku

Reputation: 1420

How to define self referencing extendable interface with optional type?

Let's say I have menu interface:

interface IMenuBase {
    id: number;
    title: string;
    items: IMenuBase[]; <- children 
}

Now I want to extend it. For this purpose I define another interface:

interface ICustomMenu extend IMenuBase {
    concrete: string;
}

The probleme is, items is still an IMenuBase.

I've tried to solve it this way:

interface IMenuBase<T extends IMenuBase<T>> {
    id: number;
    title: string;
    items: T[];
}

interface ICustomMenu extends IMenuBase<ICustomMenu> {
    concrete: string;
}

But now I want to be able to use IMenuBase without type. Here is my playground.

Upvotes: 1

Views: 92

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250366

You have two possibilities. You can go with your current solution of making the base class generic and add a default for the type parameter:

interface IMenuBase<T extends IMenuBase<T> = IMenuBase<T>> {
    id: number;
    title: string;
    items: T[];
}

interface ICustomMenu extends IMenuBase<ICustomMenu> {
    concrete: string;
}

const testBase = {} as IMenuBase;
testBase.items[0].items[0].id;

const testConcrete = {} as ICustomMenu;
testConcrete.items[0].items[0].concrete;

The above code has the issue that it only works for up to 3 levels (I think this the recursive-ness of the default is limited by the compiler)

A better option might be to use polymorphic this to specify that the children are of the same type as the current type, whatever that type may be:

interface IMenuBase {
    id: number;
    title: string;
    items: this[];
}

interface ICustomMenu extends IMenuBase {
    concrete: string;
}

const testBase = {} as IMenuBase;
testBase.items[0].items[0].id;

const testConcrete = {} as ICustomMenu;
testConcrete.items[0].items[0].concrete;

Upvotes: 2

Related Questions