Home > Blockchain >  In typescript, how do you define a self referencing recursive data structure?
In typescript, how do you define a self referencing recursive data structure?

Time:02-20

I have a self referencing recursive data structure that can look like any of the following:

const a = {
  s: "a",
  n: 0,
}

const a = {
  s: "a",
  n: 0,
  userDefinedName: {
    s: "b",
    n: 1,
  }
}

const a = {
  s: "a",
  n: 0,
  userDefinedName: {
    s: "b",
    n: 1,
    someName: {
      s: "c"
      n: 2,
    }
  }
}

const a = {
  s: "a",
  n: 0,
  userDefinedName: {
    s: "b",
    n: 1,
  },
  userDefinedName2: {
    s: "c",
    n: 2,
  }
}

I can't seem to figure out how to type it. Below are a few attempts with the typescript errors I receive in comments.

type A = {
  // Property 's' of type 'string' is not assignable to 'string' index type 'A'.
  s: string;
  // Property 'n' of type 'number' is not assignable to 'string' index type 'A'.
  n: number;
  [key: string]: A;
};


type A = {
  s: string;
  n: number;
};

type B = {
  [K in keyof A]: A[K];
  // Error: A mapped type may not declare properties or methods.
  [key: string]: B;
};

I'm sure I'm missing something obvious, but cannot seem to figure it out, hopefully someone can point me in the right direction and help explain what I'm missing? Thanks.

CodePudding user response:

You can define it using two types: a base type and the recursive type: an intersection of the base and a type having optional string properties which reference itself:

TS Playground

type Base = {
  s: string;
  n: number;
};

type Example = Base & Partial<{ [key: string]: Example }>;

declare const example: Example;
example.s; // string
example.n; // number
example.somethingElse; // Example | undefined
example.anotherProp; // Example | undefined

example.prop?.s; // string | undefined
example.prop?.n; // number | undefined
example.prop?.prop; // Example | undefined

// etc...

CodePudding user response:

You were close in your second attempt, you can accomplish this via creating a union type AB and intersecting B with A:

type A = {
  s: string;
  n: number;
}

type AB = A | string | number

type B = A & {
  [key: index]: AB
}

By putting in a filler type AB, you can handle the occurrence of deeply nested objects in the shape of A

You can see this working in this typescript playground

  • Related