I'm trying to use the following pattern:
enum Option {
ONE = 'one',
TWO = 'two',
THREE = 'three'
}
interface OptionRequirement {
someBool: boolean;
someString: string;
}
interface OptionRequirements {
[key: Option]: OptionRequirement;
}
This seems very straightforward to me, however I get the following error:
An index signature parameter type cannot be a union type. Consider using a mapped object type instead.
What am I doing wrong?
key
can only be string, number or symbol. enum is not.
You can use TS "in" operator and do this:
enum Options {
ONE = 'one',
TWO = 'two',
THREE = 'three',
}
interface OptionRequirement {
someBool: boolean;
someString: string;
}
type OptionRequirements = {
[key in Options]: OptionRequirement; // Note that "key in".
}
The simplest solution is to use Record
type OptionRequirements = Record<Options, OptionRequirement>
You can also implement it yourself as:
type OptionRequirements = {
[key in Options]: OptionRequirement;
}
This construct is only available to type
, but not interface
.
The problem in your definition is saying the key of your interface should be of type Options
, where Options
is an enum, not a string, number, or symbol.
The key in Options
means "for those specific keys that's in the union type Options".
type
alias is more flexible and powerful than interface
.
If your type does not need to be used in class, choose type
over interface
.
[key in Options]: OptionRequirement | undefined
keyof Options
within the Record
typing, it works fine, but if you do it via the "implement it yourself" route you get a syntax error, unable to do [keyof Options]: OptionRequirement
. In my case, my Option
is a type, not an enum.
Exclude
you can constrain which members should be implemented in your Record
!
In my case:
export type PossibleKeysType =
| 'userAgreement'
| 'privacy'
| 'people';
interface ProviderProps {
children: React.ReactNode;
items: {
// ↙ this colon was issue
[key: PossibleKeysType]: Array<SectionItemsType>;
};
}
I fixed it by using in
operator instead of using :
~~~
interface ProviderProps {
children: React.ReactNode;
items: {
// ↙ use "in" operator
[key in PossibleKeysType]: Array<SectionItemsType>;
};
}
I had some similar problem but my case was with another field property in interface so my solution as an example with optional field property with an enum for keys:
export enum ACTION_INSTANCE_KEY {
cat = 'cat',
dog = 'dog',
cow = 'cow',
book = 'book'
}
type ActionInstances = {
[key in ACTION_INSTANCE_KEY]?: number; // cat id/dog id/cow id/ etc // <== optional
};
export interface EventAnalyticsAction extends ActionInstances { // <== need to be extended
marker: EVENT_ANALYTIC_ACTION_TYPE; // <== if you wanna add another field to interface
}
Instead of using an interface, use a mapped object type
enum Option {
ONE = 'one',
TWO = 'two',
THREE = 'three'
}
type OptionKeys = keyof typeof Option;
interface OptionRequirement {
someBool: boolean;
someString: string;
}
type OptionRequirements = { // note type, not interface
[key in OptionKeys]: OptionRequirement; // key in
}
In my case I needed the properties to be optional, so I created this generic type.
type PartialRecord<K extends string | number | symbol, T> = { [P in K]?: T; };
Then use it as such:
type MyTypes = 'TYPE_A' | 'TYPE_B' | 'TYPE_C';
interface IContent {
name: string;
age: number;
}
interface IExample {
type: string;
partials: PartialRecord<MyTypes, IContent>;
}
Example
const example : IExample = {
type: 'some-type',
partials: {
TYPE_A : {
name: 'name',
age: 30
},
TYPE_C : {
name: 'another name',
age: 50
}
}
}
I had a similar issue. I was trying to use only specific keys when creating angular form validators.
export enum FormErrorEnum {
unknown = 'unknown',
customError = 'customError',
}
export type FormError = keyof typeof FormErrorEnum;
And the usage:
static customFunction(param: number, param2: string): ValidatorFn {
return (control: AbstractControl): { [key: FormErrorEnum]?: any } => {
return { customError: {param, param2} };
};
}
This will allow for 1 - X number of keys to be used.
I faced a similar issue, I've found this simple solution that for me, worked like a charm: simply replace [] with ():
interface OptionRequirements {
(key: Option): OptionRequirement;
}
In my case I needed to use a union type instead of an enum, but both work fine for me:
type DocumentId = 'invoiceId' | 'orderId';
export interface INote {
id: number;
organizationId: number;
(key: DocumentId): number;
//...
}
Success story sharing
interface OptionRequirements
totype OptionRequirements