Adrian Adkison
Adrian Adkison

Reputation: 3757

How to type a function that accepts an object and an array of objects that reference the object props and values?

Here is an example below and the problem is highlighted in the comments.

function formatObjectValues<T, K extends keyof T>(
  obj: T,
  formatConfig: {
    prop: K;
    format: (arg: T[K]) => string;
  }[]
) {
  return formatConfig.map(({ prop, format }) => {
    return format(obj[prop]);
  });
}

const obj = { wheels: ["good", "bad", "great", "new"], lights: 2, doors: "new" };

formatObjectValues(obj, [
  {
    prop: "doors",
    format: (doors) => `${doors}`,
    // PROBLEM: doors inferred as being string | number | string[] but should only be string
  },
  {
    prop: "wheels",
    format: (wheels) => `${wheels}`,
    // PROBLEM: wheels inferred as being string | number | string[] but should only be string[]
  },
  {
    prop: "lights",
    format: (lights) => `${lights}`,
    // PROBLEM:  inferred as being string | number | string[] but should only be number
  },
]);

How does the type signature of formatObjectValues need to change to fix the problems mentioned above?

Upvotes: 1

Views: 166

Answers (1)

Adrian Adkison
Adrian Adkison

Reputation: 3757

OK I think I got it.

type Formatter<T, K> = K extends Extract<keyof T,string> ? { prop: K, format: (val: T[K]) => string } : never

function formatObjectValues<T,K extends Extract<keyof T, string>>
(
  obj: T,
  formatConfig: Formatter<T, K>[]
) {
  return formatConfig.map(({ prop, format }) => {
    return format(obj[prop]);
  });
}

const obj = { wheels: ["good", "bad", "great", "new"], lights: 2, doors: "new" };

formatObjectValues(obj, [
  {
    prop: "doors",
    format: (doors) => `${doors}`,
  },
  {
    prop: "wheels",
    format: (wheels) => `${wheels}`,
  },
  {
    prop: "lights",
    format: (lights) => `${lights}`,
  },
]);

Upvotes: 1

Related Questions