Mark Rozovsky
Mark Rozovsky

Reputation: 13

Generic interface keys from other interface key values (typescript)

I want to create an interface that limits me to the values received from another, My Original thought was to derive from the values such as

interface Foo = {
   id: string
} 
interface Bar = {
  [values of id]
}
const FooObject: Foo = {
   id: "what",
}

const BarObject: Bar = {
  what: "whatever" // Pass
  wha: "whatever" // Fail
}

Then tried the other way around and failed as well

interface Bar = {
  [ANY KEY]: string | number;
  onClick: () => void; // need this since react 
}

interface Foo = {
   id: [keys of Bar] 
} 

const BarObject: Bar = {
  what: "whatever",
  wha: "whatever" ,
}

const FooObject: Foo = {
   id: "what", // will allow only "what" | "wha"
}

Please help me if this is even possible, I'm pretty sure I've seen a solution like this using generics

Upvotes: 1

Views: 1365

Answers (1)

jsejcksn
jsejcksn

Reputation: 33931

TypeScript does not infer literal values in object types, so the type of id in FooObject is simply string (and not "what").

You can use a const assertion to force the compiler to infer literal, readonly values, like in the example below. By doing so, you can then use the inferred type of FooObject in place of the constrained generic T in Bar to achieve the goal that you described.

See also:

TS Playground

type Foo = {
  id: string;
};

type Values<T> = T[keyof T];

type Bar<T extends Record<PropertyKey, string>> = Record<Values<T>, unknown>;

const FooObject = {
  id: "what",
} as const satisfies Foo;
//^^^^^^^^
// Using a const assertion forces the compiler to infer literal values

const BarObject = {
  what: "whatever", // OK
  wha: "whatever", /* NOK
  ~~~~~~~~~~~~~~~
  Type '{ what: string; wha: string; }' does not satisfy the expected type 'Bar<{ readonly id: "what"; }>'.
  Object literal may only specify known properties, but 'wha' does not exist in type
  'Bar<{ readonly id: "what"; }>'. Did you mean to write 'what'? (1360) */
} satisfies Bar<typeof FooObject>;

Upvotes: 3

Related Questions