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
getModewhose 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.
