Home > database >  Element implicitly has an 'any' type when accessing object literal with a non-complete str
Element implicitly has an 'any' type when accessing object literal with a non-complete str

Time:06-26

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

Code on TypeScript Playground

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
}

Code on TypeScript Playground

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

or Record Utility Type:

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.

  • Related