3

Given the following type:

type FromValues<T> = T extends Record<infer K, infer V>
  ? V extends string
    ? { [Key in keyof V]: string }
    : never
  : never

I was expecting to get a type which keys are taken from the type of the passed object of type T like:

const mapping = {
  cluster: 'CLUSTER_DATA_PLATFORM',
  subcluster: 'SOTTOCLUSTER_DATA_PLATFORM',
} as const

So, FromValues should generate types like

{
    CLUSTER_DATA_PLATFORM: string
    SOTTOCLUSTER_DATA_PLATFORM: string
}

However what I get is the following type

"CLUSTER_DATA_PLATFORM" | "SOTTOCLUSTER_DATA_PLATFORM"

What am I doing wrong?

3
  • 1
    Can you not just do it like this? Commented Aug 11, 2022 at 12:55
  • Yes it could be a solution but I have an Object Commented Aug 11, 2022 at 13:13
  • 1
    Well, although slightly different, this also works for objects. Commented Aug 11, 2022 at 14:01

1 Answer 1

2

First of all, you don't want [Key in keyof V] because V is a some string-like type; keyof V will be something like "length" | "substring" | ..., all the keys with which you can index into a string. You just want to iterate over the union members of V themselves, like [K in V].

Then when you wrote V extends string ? ... : you were accidentally making a distributive conditional type, which would result in { CLUSTER_DATA_PLATFORM: string } | { SOTTOCLUSTER_DATA_PLATFORM: string } instead of the desired result. You can fix that by wrapping the check in a one-tuple like [V] extends [string] ? ... :. That gives you this:

type FromValues<T> = T extends Record<infer K, infer V>
  ? [V] extends [string] ? { [Key in V]: string } : never
  : never

which works:

type Z = FromValues<typeof mapping>;
/* type Z = {
    CLUSTER_DATA_PLATFORM: string;
    SOTTOCLUSTER_DATA_PLATFORM: string;
} */

But you could simplify to avoid conditional types entirely:

type FromValues<T extends Record<keyof T, string>> =
  { [K in T[keyof T]]: string };

which gives the same result.

Obviously there are edge cases, if you try to pass types to FromValues where some of the value types are not strings, you'll get different behaviors, and you have to figure out what the desired behavior should be and implement something that works that way. That's out of scope of the question as asked, though.

Playground link to code

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.