Kai Sellgren
Kai Sellgren

Reputation: 30202

Type based on another field within an array

I want the following example to produce a type error:

interface Person {
  age: number;
  name: string;
  birthday: Date;
}

interface Grid<T> {
  columns: {
    field: keyof T;
    formatter: (value: T[keyof T]) => string;
  }[];
}

function draw<T>(grid: Grid<T>) {}

draw<Person>({
  columns: [
    {
      field: "age",
      formatter: (value: number) => "",
    },
    {
      field: "name",
      formatter: (value: number) => "", // <-- this parameter should be a `string`! However, TS allows this because `T[keyof T]` matches.
    },
  ],
});

To what should I change the type signature for the formatter function so that its parameter matches the type of the field?

Upvotes: 0

Views: 563

Answers (1)

Maciej Sikora
Maciej Sikora

Reputation: 20132

Mapped types will do:

interface Person {
  age: number;
  name: string;
  birthday: Date;
}

// here using mapped types 
type Grid<T, Out> =
    {
        columns: {
            [K in keyof T]: 
                {
                    field: K,
                    formatter: (value: T[K]) => Out
                }
        }[keyof T][]
    }

// pay attention that it has second argument which mean the output of the formatting
function draw<T>(grid: Grid<T, string>) {}

draw<Person>({
  columns: [
    {
      field: "age",
      formatter: value => "" // value inferred as number 
    },
    {
      field: "name",
      formatter: (value: number) => "", // error as expected
    },
  ],
});

playground

Some explanation of:

type Grid<T, Out> =
    {
        columns: {
            [K in keyof T]: 
                {
                    field: K,
                    formatter: (value: T[K]) => Out
                }
        }[keyof T][]
    }
  • we map over keys of T K in keyof T
  • in every iteration we make an object which has wanted key K and wanted function from T[K] to defined output type
  • [keyof T] means that we want all values from created object by the mapped type, in result we will get union of all object with field and formatter props

Upvotes: 3

Related Questions