Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to constrain type to be a key in a generic object who's value is a string

Tags:

typescript

I have an interface called MyInterface that looks like this:

interface MyInterface<T, K extends keyof T> {
  key: K
  data: Array<T>
}

I want to constrain the key prop to be only keys that are present in T and also where the keys in T are only strings. The above interface constrains the key, but not its value to only strings. How can I add this constraint? Some examples of the behavior I'm looking for:

interface Data {
  foo: string
  bar: number
}

const d: Data[] = [{ foo: 'xxx', bar: 1 }]

// this is valid as the key is in the Data interface and the value of the key is a string
const a: MyInterface<Data, 'foo'> = { key: 'foo', data: d }

// this should be a type error as the value of bar is a number, not a string
const b: MyInterface<Data, 'bar'> = { key: 'bar', data: d } 

// this should also be a type error as the key is not in the object
const c: MyInterface<Data, 'baz'> = { key: 'baz', data: d }
like image 910
turtle Avatar asked Oct 24 '25 08:10

turtle


1 Answers

You would like to ensure that in MyInterface<T, K>, the T type has a string property at key K. There are a few ways to write this. For example, you could constrain K to KeysMatching<T, string> where KeysMatching is described in the answer to In TypeScript, how to get the keys of an object type whose values are of a given type?.

But it's even more straightforward to constrain T to Record<K, string> (using the Record<K, V> utility type):

interface MyInterface<T extends Record<K, string>, K extends keyof T> {
    key: K
    data: Array<T>
}

Then your examples work as desired:

const a: MyInterface<Data, 'foo'> = { key: 'foo', data: d }; // okay
const b: MyInterface<Data, 'bar'> = { key: 'bar', data: d }; // error!
// ----------------> ~~~~
// Type 'Data' does not satisfy the constraint 'Record<"bar", string>'.
// Types of property 'bar' are incompatible.
const c: MyInterface<Data, 'baz'> = { key: 'baz', data: d }; // error!
// ----------------> ~~~~
// Type 'Data' does not satisfy the constraint 'Record<"baz", string>'.

Playground link to code

like image 75
jcalz Avatar answered Oct 25 '25 22:10

jcalz