в TypeScript, как я могу создать расширяемый универсальный тип с потомками того же типа, с типом по умолчанию? - PullRequest
1 голос
/ 06 июня 2019

Сначала у меня есть такой тип:

type Foo = {
    bar: string;
    children: Foo[];
};

Теперь я хочу разрешить расширение типа Foo, чтобы пользователи могли добавлять свойства, но при этом предоставляли как минимум bar иchildren.Я подумал, что могу легко это сделать:

interface Foo<T extends Foo<T>> {
    bar: string;
    children: T[];
}

Но потом я попытался объявить такой тип ... и я не могу: requires 1 type arg

Это бесконечный цикл ...

Итак, я пытаюсь использовать параметр универсального типа по умолчанию, но опять же, я не могу ... circular default

У меня пердит мозг?или это невозможно?

Я пытаюсь передать:

  • тип Foo расширяемый
  • тип Foo имеет childrenсвойство того же типа, что и сам тип
  • полная безопасность типов (поэтому нет any везде - каламбур)

Ответы [ 3 ]

1 голос
/ 06 июня 2019

Вы можете добавить пустой интерфейс JustFoo, чтобы избежать бесконечного цикла:

interface Foo<T> {
    bar: string;
    children: T[];
}

interface JustFoo extends Foo<JustFoo> {
}

interface Bar extends Foo<Bar> {
    barProp: number;
}

const bar: Bar = {
    barProp: 123,
    bar: 'aaa',
    children: [{ barProp: 123, bar: 'aaa', children: []}]
}

const foo: JustFoo = {
    bar: 'aaa',
    children: [{  bar: 'aaa', children: []}]
}
1 голос
/ 06 июня 2019

Зависит от того, как вы хотите расширить тип Foo.Если вы хотите добавлять и получать доступ к свойствам ad hoc (в любое время в коде), Foo должен иметь «сигнатуры строкового индекса» в своем интерфейсе:

type Foo = {
    bar: string;
    children: Foo[];
    [key: string]: any; };

let fooInstance: Foo; 
fooInstance.bar = "toto"; 
fooInstance.newProperty = 24;

Vscode pic:

enter image description here

Вы также можете расширить Foo, используя другие интерфейсы, через наследование.

interface FooExtended extends Foo {
    age: number;
    street: string;
}

let extendedFoo : FooExtended;
extendedFoo.age = 22;
extendedFoo.bar = "popo";

Вы можете переопределить интерфейс, чтобы сопоставить массив детей с новыминтерфейс:

interface FooExtended extends Foo {
    age: number;
    street: string;
    children: FooExtended[];
}
0 голосов
/ 07 июня 2019

Хорошо, спасибо Lombas и aquz за то, что указали мне правильное направление.Как и много раз раньше, я пытался упростить свою проблему, чтобы поделиться ею здесь, и при этом я упрощал ее.

Моя более точная проблема заключалась в следующей структуре:

export type NavigationEntry = NavigationEntryWithChildren | NavigationEntryWithoutChildren;

interface NavigationEntryBase {
    display: string;
    exact?: boolean;
}
interface NavigationEntryWithChildren extends NavigationEntryBase {
    children: NavigationEntry[];
}

interface NavigationEntryWithoutChildren extends NavigationEntryBase {
    routerLinkInfo: any[] | string;
}

КакНазвание указывает, что речь идет о создании навигационной структуры.На любом уровне, у вас есть URL (routerLinkInfo) или , у вас есть дети.Поэтому мне пришлось использовать типы union , а не просто интерфейсы.

Хотя я занимаюсь TypeScript полный рабочий день в течение 3 лет, мой ум иногда застревает в более традиционном объектно-ориентированном типетакие системы, как C #, и забывают, что TypeScript просто заставит его работать .

Так что вместо того, чтобы возиться с NavigationEntry и заставить его работать напрямую, я создал несколько дополнительных типов:

interface CustomNavigationEntryWithChildren<T> extends NavigationEntryWithChildren {
    children: CustomNavigationEntry<T>[];
}

export type CustomNavigationEntry<T> =
    | (CustomNavigationEntryWithChildren<T> & T)
    | (NavigationEntryWithoutChildren & T);

При таком подходе весь существующий код, для которого требуется только NavigationEntry, будет работать.А более новые проекты, которые хотят добавить дополнительные свойства в свои узлы навигации, могут делать это следующим образом:

type Foo = CustomNavigationEntry<{foo: boolean}>;

В приведенном выше примере все узлы должны иметь логическое значение foo.

Надеюсь, что это поможет следующему человеку (даже если это только я;))

...