6

Is there a way to write an interface for an object that has a single dynamically-named key?

I'm able to write an interface that accepts any number of dynamically-named keys, but I'd like to restrict it to just one.

Let's start with some basics, and work our way up to my problem. In the following interface, the object can only have a single key, and it is named "id":

interface Test {
  id: string
}

This is good in that objects with this interface can only have one property, id. But I need the consumer to be able to specify the name of this key.

If I change that interface to be the following, it allows a consumer to specify a custom key:

type Test<K extends string> = {
  [P in K]: string
}

This gets me closer to what I am looking for, as we can see in this example:

type SpecificTest = Test<"customId">;

const test:SpecificTest = {
  customId: 'pls',
}

However, the user can pass a union type to define multiple ID fields, and that's where things go awry.

// I don't want a user to be able to pass multiple strings here
type SpecificTest = Test<"customId"|"anotherId">;

const test:SpecificTest = {
  customId: 'pls',

  // :(
  anotherId: 'blah'
}

I was thinking something along these lines might do the trick (in pseudocode):

type Test<K extends string> = {
  [K]: string
}

but that specific syntax doesn't work.

Is there any way to define an interface where a user can only define a single dynamically-named key?

jamesplease
  • 12,547
  • 6
  • 47
  • 73

1 Answers1

6

You can detect if the passed type is a union by IsUnion helper type

type Test<K extends string> = IsUnion<K> extends true ? Error<'Please don\'t pass a union'>  : {
  [P in K]: string
}

interface Error<M> {msg: M}
Nurbol Alpysbayev
  • 19,522
  • 3
  • 54
  • 89