Home > database >  Dynamic Interface from own property
Dynamic Interface from own property

Time:03-05

I wanted to know if this is possible on TypeScript.

I have an interface like this:

interface ComplexTable extends BaseTable {
    headers: HeaderOptions[];
    datas: { [id: string]: string }[];
  }

And HeaderOptions is just an interface with a label and an id

Is there a way to only allow on datas headers ids given before?

For example I have

const table: ComplexTable = {
  headers: [
    { id: 'a', label: 'Name' },
    { id: 'b', label: 'Last Name' },
  ]
}

I wan't to only allow on datas objects with only the keys 'a' and 'b'.

This should work

const table: ComplexTable = {
  headers: [
    { id: 'a', label: 'Name' },
    { id: 'b', label: 'Last Name' },
  ],
  datas: someData.map((data) => ({
    a: data.someValue,
    b: data.someOtherValue
  }))
}

This should not work

const table: ComplexTable = {
  headers: [
    { id: 'a', label: 'Name' },
    { id: 'b', label: 'Last Name' },
  ],
  datas: someData.map((data) => ({
    a: data.someValue,
    c: data.someOtherValue
  }))
}

CodePudding user response:

You can do this, but it's going to take some work:

  • You'll need to parameterize ComplexTable so it can apply the same rules to its members headers and datas.
  • You'll need to parameterize HeaderOptions so it can read the keys out of id rather than treating them as a plain string.
  • You'll need to specify datas to take the mapped type [id in K].
  • You'll need an identity function so TypeScript can infer the parameters as you construct the table.

That would look like this:

interface HeaderOptions<K> {
  id: K;
  label: string;
}

interface ComplexTable<K extends string> extends BaseTable {
  headers: HeaderOptions<K>[];
  datas: { [id in K]: string }[];
}

function complexTable<K extends string>(table: ComplexTable<K>) {
  return table;
}

So you can invoke it like this:

const shouldWork = complexTable({
  headers: [
    { id: 'a', label: 'Name' },
    { id: 'b', label: 'Last Name' },
  ],
  datas: someData.map((data) => ({
    a: data.someValue,
    b: data.someOtherValue
  }))
});

Playground link

Note that, as in the playground, TypeScript won't mind if you supply more data keys than it knows to expect in the headers; however, it will complain if any of the data keys is missing.

  • Related