1

I don't know what exactly to call this problem but this is my case.

I want defaultValue to automatically get type based on id value.

type User = {
  name: string;
  age: number;
};

type PropConfig<T, K extends keyof T> = {
  id: K;
  label: string;
  defaultValue: T[K];
};

const config: PropConfig<User, 'age'> = {
  id: 'age',
  label: 'Age',
  defaultValue: 18,
};

This is what I want:

type PropConfig<T, K = keyof T> = {
  id: K;
  label: string;
  defaultValue: T[K]; // Error here
};

const config: PropConfig<User> = {
  id: 'age',
  label: 'Age',
  defaultValue: 18,
};

Can someone help me?

1 Answer 1

2

TypeScript doesn't have partial type argument inference as proposed in ms/TS#26242 so there's no way to specify T while having the compiler infer K, at least not without workarounds involving curried helper functions or dummy parameters (see Typescript: infer type of generic after optional first generic for more info).

Luckily, I don't think you want PropConfig to be generic in K. Instead, you want PropConfig<T> to be a union of the possible types for each K in keyof T. That is, you want to distribute the type across unions in keyof T. There are a number of ways to do that, but the one I usually do is to make what's called a distributive object type, as coined in ms/TS#47109. It looks like this:

type PropConfig<T> = { [K in keyof T]-?: {
  id: K;
  label: string;
  defaultValue: T[K];
} }[keyof T]

I immediately index into a mapped type, producing the following for PropConfig<User>:

type PropConfigUser = PropConfig<User>;
/* type PropConfigUser = {
    id: "name";
    label: string;
    defaultValue: string;
} | {
    id: "age";
    label: string;
    defaultValue: number;
} */

which is the type you want:

const config: PropConfig<User> = {
  id: 'age',
  label: 'Age',
  defaultValue: 18,
};

Playground link to code

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

1 Comment

This was really helpful, but led to me finding what I think is a bug. github.com/microsoft/TypeScript/issues/50147

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.