Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: Look up field's type based on another field

Tags:

typescript

Let's say I have an interface like this:

interface Cat {
  weight: number;
  name: string;
  adoptable: boolean;
}

I want to define the following interface that can be used to set values on Cat:

interface CatUpdate {
  field: keyof Cat;
  value: ???
}

and I want TypeScript to enforce that value is the appropriate type based on field. E.g., this should be invalid:

const x: CatUpdate = {
  field: "name",
  value: 12
}

How do I define the type of CatUpdate.value?

like image 315
erjiang Avatar asked Aug 07 '19 04:08

erjiang


1 Answers

My inclination would be to make CatUpdate a union type and not an interface, like this:

interface Cat {
  weight: number;
  name: string;
  adoptable: boolean;
}

type PropUpdate<T extends object> = {
  [K in keyof T]: { field: K; value: T[K] }
}[keyof T];


type CatUpdate = PropUpdate<Cat>;
/* 
type CatUpdate = {
    field: "weight";
    value: number;
} | {
    field: "name";
    value: string;
} | {
    field: "adoptable";
    value: boolean;
}
*/

That should work well enough on your mentioned use case:

const x: CatUpdate = {
  field: "name",
  value: 12
} // error!

And you should be able to compose those (CatUpdate | DogUpdate) if you need to, although without a particular use case it's hard to know if such composition is appropriate (Can Cat and Dog be discriminated from each other? That is, is Cat & Dog impossible? If Cat & Dog is possible, then CatUpdate | DogUpdate can only be used to update a Cat & Dog. If it's impossible, then CatUpdate | DogUpdate should probably be replaced with a discriminated union instead like (CatUpdate & {kind: "CatUpdate"}) | (DogUpdate & {kind: "DogUpdate"}).)

Hope that helps; good luck!

Link to code

like image 133
jcalz Avatar answered Nov 14 '22 17:11

jcalz