pistacchio
pistacchio

Reputation: 58863

Typescript declaration of a Record with a limited set of fields

I have the following code:

type MyDict = {
  [k: string]: unknown;
};

function fn<T extends MyDict>(items: T) {
  const functionDict: { [k: string]: (val: keyof T) => void } = Object.fromEntries(
    Object.keys(items).map((key: keyof T) => [
      key,
      (val: keyof T) => console.log(val),
    ]),
  );
}

is there a way to declare functionDict as an object (or a Record<>) whose keys are explicitly key of T instead of the generic string?

For instance, if I call

type MyExtendedDict = MyDict & {
  foo: number;
  bar: number;
}

fn<MyExtendedDict>({
  foo: 1,
  bar: 2
})

I want to make explicit that the keys of the dictionary would be "foo" and "bar".

Upvotes: 0

Views: 86

Answers (1)

jperl
jperl

Reputation: 5112

No matter what, it seems that Object.fromEntries will always have string as keys.

A possible solution:

function fn<T extends Record<string, any>>(items: T) {
  const entries = new Map<keyof T, (val: T[keyof T]) => void>(Object.keys(items).map((key: keyof T) => [
      key,
      (val: T[keyof T]) => console.log(val)
    ]))
    
  const functionDict = Array.from(entries).reduce((obj, [key, value]) => {
    obj[key] = value
    return obj
  }, {} as Record<keyof T, (val: T[keyof T]) => void>);

  return functionDict
}

fn({
  foo: 1,
  bar: 2
})

What we get from Typescript:

function fn(items: {
    foo: number;
    bar: number;
}): Record<"foo" | "bar", (val: number) => void>

playground

Upvotes: 1

Related Questions