My app has list of status
literal strings. Some of these statuses have an associated icon name, but not all of them.
I have a function that returns an icon name for a status
, or null
if no icon name exists.
type StatusType = "Request" | "Option" | "Confirmed" | "Cancelled" | "Created";
const icons = {
Request: "clock",
Confirmed: "check",
Cancelled: "cross",
} as const;
function iconNameForStatus(status: StatusType) {
return icons[status];
}
TypeScript gives me this error, which I don't understand:
Element implicitly has an 'any' type because expression of type 'StatusType' can't be used to index type '{ readonly Request: "clock"; readonly Confirmed: "check"; readonly Cancelled: "cross"; }'.
Property 'Option' does not exist on type '{ readonly Request: "clock"; readonly Confirmed: "check"; readonly Cancelled: "cross"; }'.(7053)
How do I fix this error?
In the real code, the StatusType
is a union from multiple imported objects, so I'd rather not have icons
contain an exhaustive list of all possible keys.
I tried checking for the key before accessing the object, but the error is still there:
type StatusType = "Request" | "Option" | "Confirmed" | "Cancelled" | "Created";
const icons = {
Request: "clock",
Confirmed: "check",
Cancelled: "cross",
} as const;
function iconNameForStatus(status: StatusType) {
if (status in icons) {
return icons[status]
}
return null
}
CodePudding user response:
When a raw object is defined in Typescript (e.g. icons
) - it won't allow for indexed access (e.g. icons[key]
) where key is a string or in your case string litral.
Either icons[status]
should be of type keyof typeof icons
or you need to redefine icons to allow for indexing by a random string, but that is less typesafe, since you aren't guaranteeing that you will pass in a valid key.
You can use Index Signature
:
const icons : {[key:string]:string} = {
Request: "clock",
Confirmed: "check",
Cancelled: "cross",
}
const icons : Record<string, string> = {
Request: "clock",
Confirmed: "check",
Cancelled: "cross",
}
CodePudding user response:
You can redefine icons
to only allow for values that are in the StatusType
Union. You'll need to Exclude
the values that you don't have entries for and then add specific checks for them inside iconNameForStatus
.
Something similar to this:
type StatusType = "Request" | "Option" | "Confirmed" | "Cancelled" | "Created";
const icons: Record<Exclude<StatusType, "Option" | "Created">, string> = {
Request: "clock",
Confirmed: "check",
Cancelled: "cross",
} as const;
function iconNameForStatus(status: StatusType) {
if (status === "Option" || status === "Created") {
return null
}
return icons[status];
}
It would be a lot more readable if you would give default values for missing StatusType
. Something similar to this:
type StatusType = "Request" | "Option" | "Confirmed" | "Cancelled" | "Created";
const icons: Record<StatusType, string> = {
Request: "clock",
Confirmed: "check",
Cancelled: "cross",
Option: "default",
Created: "default"
} as const;
function iconNameForStatus(status: StatusType) {
return icons[status];
}
This removes the need for Exclude
and the extra check inside iconNameForStatus
.