18
votes

Is it possible to use a union type as a key in an interface? For example, I want to do something like this:

interface IMargin {
  [key in 'foo' | 'bar']: boolean;
}

But I'm getting this error:

A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)

Is there any way around this?

The use case is turning an array of values into an interface:

const possibleTypes = ['foo', 'bar'];
interface Types {
    foo?: boolean;
    bar?: boolean;
}
4
Maybe keyof Types? - Sergiu Paraschiv

4 Answers

28
votes

You can use an object type instead of an interface, which are mostly interchangeable:

type IMargin = {
    [key in 'foo' | 'bar']: boolean;
}
3
votes

This is not necessarily an answer, but I think this may interest others.

You can use a Union Type as a key in a sub-property of an Interface.

export type Language = 'EN' | 'DE' | 'IT';

export interface Document {
  generic: string;
  languages: {
    [key in Language]: string[];
  }
}
2
votes

Interfaces do not support mapped types (which is what you are looing for here). You can use a type alias, for most uses it should work the same (not all cases but if the types are known you can implement the type alias just as you would the interface).

const possibleTypes = (<T extends string[]>(...o: T) => o)('foo', 'bar');
type Types = Record<typeof possibleTypes[number], boolean>

The mapped type Record should work will here

1
votes

It's possible to map over the union into aType instead of an Interface as an option.

type Language = "EN" | "DE";

type Types<T> = {
  [K in Strings]?: boolean
}