2

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 }
Agus Neira
  • 435
  • 4
  • 9
turtle
  • 7,533
  • 18
  • 68
  • 97

1 Answers1

1

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

jcalz
  • 264,269
  • 27
  • 359
  • 360