Hur definierar man ett komplext trädstruktursgränssnitt i TypeScript?


Jag hade lärt mig några typiska gränssnitt för enkel trädstruktur, som:

export type Tree<T> = T & {
  children?: T[];
}

Men orolig när bladnodens attribut skiljer sig från föräldranodens attribut.

I min användningssituation:

  • Föräldranod har ett attribut: groupName

  • Bladnod har två attribut: fileName och uniqueId

Så jag definierar trädstrukturen så här:

export type Tree = {
  isGroup: true;
  groupName: string;
  children: Tree[];
} | {
  isGroup: false;
  fileName: string;
  uniqueId: number;
}

Enligt min åsikt finns det 2 nackdelar med denna typ av trädstruktur:

  1. Det är nödvändigt att noggrant fastställa om varje nod är Group i rekursiv traversal. Däremot är det bättre att noggrant avgöra det endast med children. (det ser ut som om isGroup är lite redundant)

  2. Inga mallbokstavstyper

Finns det ett bättre sätt att hantera detta scenario?

Svar #1

Jag skulle ha hanterat det så här:

export type Branch = {
  children: Tree[];
};

export type Twig = {
  fileName: string;
  uniqueId: number;
};

type Either<X, Y> =
  | ({
      [KX in keyof X]: X[KX];
    } & {
      [KY in Exclude<keyof Y, keyof X>]?: never;
    })
  | ({
      [KY in keyof Y]: Y[KY];
    } & {
      [KX in Exclude<keyof X, keyof Y>]?: never;
    });

export type Tree = Either<Branch, Twig>;

På det här sättet behöver du inte isGroup, vilket som du påpekade är överflödigt. Och det kommer att säkerställa att det inte finns några objekt som har egenskaper från båda på det här sättet

const tree: Tree = {
    fileName: "",
    children: [
        {
            fileName: "",
            uniqueId: 1,
        }
    ]
}

och Exclude är nödvändig om båda typerna har gemensamma egenskaper

TS lekplats exempel