I have this type:
type ErrorMessages = Record<number | 'default', string>;
Then, when I define a variable as const text: ErrorMessages = {403: 'forbidden'}
, Typescript says that default
is missing in the type:
Why a Typescript Record with a union of a primitive and a specific string makes the string mandatory?
CodePudding user response:
Record
distributes the union you give for the key argument, creating an object type requiring all of those keys. In your IDE or the TypeScript playground, if you hover over ErrorMessages
, you'll see the expanded definition of it, which makes the problem clear:
type ErrorMessages = {
[x: number]: string;
default: string;
}
Similarly, Reocrd<"a" | "b", string>
requires both a
and b
properties.
Instead, you can define ErrorMessages
as an object type directly, explicitly while making default
optional via a postfix ?
, like this:
type ErrorMessages = {
[key: number]: string;
default?: string;
};
That allows both of your assignments:
const text1: ErrorMessages = { 403: "forbidden" };
const text2: ErrorMessages = { default: "something else" };
That will also allow multiple messages, which I think is correct given the type name ErrorMessages
(plural):
const text3: ErrorMessages = {
default: "something else",
403: "forbidden",
};
...while disallowing other string keys:
// Error as desired
const text4: ErrorMessages = { foo: "bar" };
// ^^^^^^^^^^ Type '{ foo: string; }' is not assignable to type 'ErrorMessages'.
// Object literal may only specify known properties, and 'foo' does not exist in type 'ErrorMessages'. (2322)
CodePudding user response:
You can use built-in Partial utility type, like so:
type ErrorMessages = Partial<Record<number | 'default', string>>