3

Let's say I have the following:

type Field = {
  _id: string;
  value: string;
}

const fields = [
  { _id: 'a', value: '2' },
  { _id: 'b', value: '3' }
] as const;

I would like to define a type ValueById<T extends readonly [...Field[]]> which produces:

{
  a: '2',
  b: '3',
}

I've gotten partially to where I want to be with this:

export type ValueById<T extends readonly [...Field[]]> = {
  [Z in T[number]['_id']]: T[number]['value']
}

type NewType = ValueById<typeof fields>

But unfortunately this produces a type of:

{
  a: '2' | '3'
  b: '2' | '3'
}

Is there a way to narrow down the type of the values here?

2
  • 1
    Is { a: '2' } & { b: '3' } good enough? typescriptlang.org/play/index.html#code/… Commented May 4, 2020 at 10:35
  • Oooh, super close. Might be good enough for my use case. I'll give it a shot, thanks! Commented May 4, 2020 at 10:49

1 Answer 1

1

First we'll need to extract array item type:

const fields = [
  { _id: 'a', value: '2' },
  { _id: 'b', value: '3' }
] as const;

type Field = typeof fields[number];
// {
//     readonly _id: "a";
//     readonly value: "2";
// } | {
//     readonly _id: "b";
//     readonly value: "3";
// }

Now we can create a union of "id-value" pairs using distributive conditional types

type IdValueUnion<T> = T extends Field ? { [Z in T['_id']]: T['value'] } : never;
// IdValueUnion<Field>
//
// {
//     a: "2";
// } | {
//     b: "3";
// }

We're pretty close, but we need intersection instead of union:

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
// UnionToIntersection<IdValueUnion<Field>>
// {
//     a: "2";
// } & {
//     b: "3";
// }

Last step is "compact" the intersection by just remapping keys to values:

export type Compact<T> = { [K in keyof T]: T[K] };

export type ValueById = Compact<UnionToIntersection<IdValueUnion<Field>>>;
// 🎉
// {
//     a: "2";
//     b: "3";
// }

let foo: ValueById = { a: '2', b: '3' }

Playground

Sign up to request clarification or add additional context in comments.

1 Comment

Nice, that compact trick is great! Very good to know

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.