Home > database >  How to define that an object can only contain specific values (depending on the key)?
How to define that an object can only contain specific values (depending on the key)?

Time:10-14

Question

How can I define a record type Shape<RootType> whose values (if specified) can only be "A" if the key can index RootType and either "B" or "C" otherwise?

Test cases

type User = {
  id: number;
  name: string;
  unused: number;
}

declare function test<T extends Shape<User>>(): void;

// The following should compile:
test<{
  id: "A",
  name: "A",
  something: "B",
  else: "C",
}>();

// The followings shouldn't compile:
test<{ id: "B" }>(); // `id` should only be "A"
test<{ name: 123 }>(); // `name` should only be "A"
test<{ something: "A" }>(); // extra keys can only be "B" or "C"

What have I tried?

type Shape<RootType> = {
  [KeyType in symbol]?:
    KeyType extends RootType
      ? "A"
      : "B" | "C"
}

I've tried the above but that type seems to extend whatever record type.

CodePudding user response:

You would have to map over the keys of T and check for each key K if it extends keyof RootType. To make T available in Shape, we need to pass it to Shape as a generic parameter too.

declare function test<T extends Shape<User, T>>(): void;

type Shape<RootType, T> = {
  [K in keyof T]:
    K extends keyof RootType
      ? "A"
      : "B" | "C"
}

// The following is valid:
test<{
  id: "A",
  name: "A",
  something: "B",
  else: "C",
}>();

// The followings aren't valid:
test<{ id: "B" }>(); // `id` should only be "A"
test<{ name: 123 }>(); // `name` should only be "A"
test<{ something: "A" }>(); // extra keys can only be "B" or "C"

Playground

  • Related