Home > Blockchain >  Typescript getting error when you have array of object and other properties in a type
Typescript getting error when you have array of object and other properties in a type

Time:01-08

I have a object like below

let Obj = {
['0'] : {
 mode: 'x'
},
getMode: () => 'x'
}

Getting error when I create type definition like below

type Obj = {

[id: string]: {
mode: string
};
getMode: () => string

}

Getting error -- Property 'getMode' of type '() => string' is not assignable to 'string' index type '{ mode: string}'. ts(2411)

CodePudding user response:

You are using index signature, so you have to specify other type with that signature.

type Obj = {
  [id: string]: { mode: string} | (() => string);
}

let Obj: Obj = {
  ['0']: {
    mode: 'x'
  },
  getMode: () => 'x'
}

CodePudding user response:

This may be counterintuitive at first, but what you have is two definitions for getMode which are mutually exclusive

Within the type Obj you have the following:

  • A generic placeholder key [id: string] which basically says "any key which is a string should have the following shape" (in this case { mode: string })
  • An explicit key getMode whose value is of the shape () => string

As the first definition should be true for any key that is a string, it therefore clashes with the second definition. In other words, getMode is a string key and therefore satisfies the constraint [id: string], meaning it must be of the shape { mode: string }.

This concept (and potential workarounds) is explored in much more depth in similar SO answers such as this one

CodePudding user response:

In Typescript [key: string]: type means that ALL of the object keys should be of that type. You can solve this with intersection

type Obj = {

[id: string]: {
mode: string
};
} & {
    getMode: () => string
}

CodePudding user response:

When you want to use a general key with { mode: string } type, all properties that you define which have string key type (including getMode) should have { mode: string } value type. One way of detaching getMode from general keys is to split your Obj type in two parts:

type GetModeType = {
  getMode: () => string;
};

type GeneralType<T extends string> = {
  [P in T as T extends "getMode" ? never : P]: { mode: string };
};

And then create a generic type based on their intersection:

type Obj<T extends string> = GetModeType & GeneralType<T>; 

Then you can create an object based on Obj type:

const obj: Obj<"0" | "pending"> = {
  getMode: () => "hello",
  ["0"]: { mode: "zero" },
  pending: { mode: "this is pending mode." },
};

Notice that Obj type has getMode property with () => string type and the other keys that you want should be passed through as a generic type(string literals union).

You can read more about generic types and mapped types in typescript documentation.

  • Related