0

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?

Kai Sellgren
  • 27,954
  • 10
  • 75
  • 87
  • I believe this answer can help you https://stackoverflow.com/questions/65129070/defining-an-array-of-differing-generic-types-in-typescript#answer-65129942 Here https://catchts.com/callbacks you can find some extra information – captain-yossarian from Ukraine Apr 22 '21 at 09:42

1 Answers1

3

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
Maciej Sikora
  • 19,374
  • 4
  • 49
  • 50
  • I ended up with very similar approach https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgArQM4HsTIN4BQyycA5hAFzIgCuAtgEbQDcR1cdlyGYUoprYg2BQwACwAmcAJ5UAInEisAvgQJhpABxQA1OABsaEDAB4AKgD5kAXmRmA2gGsI0rDDsBdNRu3IAwliGdCCmljb4bPaoyKDIzq7uZh5UhMTEMMAQ+hJUqAA0bOlYUHSKkFBUABRwUKRUDqgeAJQ2Vjx8IKRsqspOLm6eagQIODzIElBwAO7h5sgQAB6QIBIYaJg4ecgAqvNLECtrAIJQk9ImAUEh5hZWtidnF4H015a3lSNXGFT2AHT-22arWQlTwyE+LzWyiaQxGITAyFKznCE2mJnQUGwIAslXsbFSaQyWRyyAARGQIKSCmlkDBiqUwOUqgA3AxGFrWKykqnIAD0vOo9CYUG61IiNKJ2SoAHIQBwINKxUUSmVoCy2RAOVyefzuLx+KL8YVaZkpchpcJRJIZIrjXSVYy1SDWYZNcDuVtdQpIN0CM0gA – captain-yossarian from Ukraine Apr 22 '21 at 10:53