Reputation: 53311
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;
}
Upvotes: 46
Views: 44482
Reputation: 8475
This builds on the other answers, and adds an answer to a question about selectively specifying languages.
I prefer the use of Record
over the index specification ([key of/in]
) as it's a bit easier to read and understand (Record<property name type, value type>
).
The additional question:
did you get a workaround to get just one of the languages as a single key, instead of multiple possible keys?
In this case, use the Partial
helper. Wrapping anything with Partial<other type>
makes all the properties optional (able to be undefined).
export type Language = 'EN' | 'DE' | 'IT';
export type LanguageMap = Partial<Record<Language, string[]>>;
export class Document {
public generic: string;
public languages: LanguageMap;
constructor(generic: string, langMap: LanguageMap) {
this.generic = generic;
this.languages = langMap;
}
}
const x = new Document(
'test',
{
'EN': ['English', 'British English', 'Australian English', 'US English']
}
);
const y = new Document(
'test',
// this can be empty!
{
}
);
Upvotes: 1
Reputation: 147
const possibleTypes = ['foo', 'bar'] as const;
type Types = Record<typeof possibleTypes[number], boolean>
Saying as const
we declare that the array is immutable and not just Array<string>
.
possibleTypes[number]
is needed to filter out array methods, like length
from the type.
Upvotes: 0
Reputation: 349
convert possible types to enum convert interface to type since An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead
enum possibleTypes { 'foo', 'bar' };
type Types {
[key in keyof typeof possibleTypes]: boolean
}
Upvotes: 2
Reputation: 763
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[];
}
}
Upvotes: 19
Reputation: 249636
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
Upvotes: 6
Reputation: 2822
You can use an object type instead of an interface, which are mostly interchangeable:
type IMargin = {
[key in 'foo' | 'bar']: boolean;
}
Upvotes: 60