An index signature parameter type cannot be a union type. Consider using a mapped object type instead

JavascriptTypescript

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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".
}

Solution 2 - Javascript

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.

Solution 3 - Javascript

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>;
  };
}

Solution 4 - Javascript

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
}

Solution 5 - Javascript

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
}

Solution 6 - Javascript

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
        }
    }
}

Solution 7 - Javascript

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.

Solution 8 - Javascript

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;
    //...
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionjohn maccarthyView Question on Stackoverflow
Solution 1 - JavascriptNacho Justicia RamosView Answer on Stackoverflow
Solution 2 - JavascriptunionalView Answer on Stackoverflow
Solution 3 - JavascriptAmerllicAView Answer on Stackoverflow
Solution 4 - JavascriptKurkov IgorView Answer on Stackoverflow
Solution 5 - JavascriptStefanView Answer on Stackoverflow
Solution 6 - JavascriptAlazzawiView Answer on Stackoverflow
Solution 7 - JavascriptQuestioningView Answer on Stackoverflow
Solution 8 - JavascriptJoeView Answer on Stackoverflow