Automatically deduce that the variable fits within the set of keys

When attempting to access an object using a string variable, I encountered the following error message:

No index signature with a parameter of type 'string' was found on type '{ A: number; B: number; C: number; }'

const obj = {
    "A":1,
    "B":2,
    "C":3
 }

 const f=(str:string)=>{
     const result = obj[str];
 }

Shouldn't this code compile and have result return a type of number|undefined? Is casting str to keyof typeof obj the only solution, as suggested in this post? Another approach I considered was using

if (Object.keys(obj).includes(str))
... however, this does not fully restrict the type of str to valid keys.

Answer №1

In TypeScript, object types are considered "open" or "extendible" rather than "closed" or "exact". This means that while an object type must have known properties, it is not restricted from having additional, unknown properties. Open and extendible object types allow for interface and class extension to create type hierarchies. For example, defining

interface Bar extends Foo { extraProp: string }
signifies that every Bar is also a Foo, even though Foo does not define anything related to extraProp. However, this concept may have unexpected implications for developers. Currently, exact types are not fully supported, although excess property checking can sometimes give the illusion of such support. Refer to microsoft/TypeScript#12936 for more information.

Analyzed by the compiler, the type of obj is:

/* const obj: {
    A: number;
    B: number;
    C: number;
} */

This indicates that the compiler recognizes numeric properties at keys A, B, and C in obj. As object types are open, the compiler is unaware of the absence of other properties in obj. Even though the object literal assigned to obj lacks additional properties, the type system does not retain this information. Hence, according to the compiler, obj could potentially have various other properties besides A, B, and C. While accessing these defined properties would not trigger warnings with strict settings, attempting to access an unknown property using a string key may result in the compiler considering it as any.

To specify the types of unknown properties in TypeScript, one option is to use an index signature like

{[k: string]: number | undefined}
. It is not possible to declare "A, B, and C are of type number and all other properties are undefined" due to the absence of a suitable index signature mechanism for representing "all other properties". Alternatively, you could state "A, B, and C are number and all properties are number | undefined". If tracking A, B, and C is unimportant, utilizing the annotation "all properties are string | undefined" achieves similar results.

const obj: {[k: string]: number | undefined} = {
    "A": 1,
    "B": 2,
    "C": 3
};

const f = (str: string) => {
    const result = obj[str]; // number | undefined
}

The function now explicitly returns number | undefined based on the specified type.

Playground link showcasing the code

Answer №2

Encountered a similar issue not long ago, and I found a helpful solution here: Is key-value pair available in Typescript?

In your case, you could try something along these lines:

type Dictionary = {
  [key: string]: number
}

const dictionary: Dictionary = {
  "Apple": 1,
  "Banana": 2,
  "Cherry": 3
}

const findValue = (key: string) => {
  const value: number = dictionary[key];
}

There may be other approaches worth considering as well.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the significance of the any type in Typescript?

As I delve into learning Typescript, a question arises in my mind. I find myself pondering the purpose of the any type. It seems redundant to specify it when every variable essentially acts as an "any" type by default. Consider this scenario where the out ...

What is the method for defining specific requirements for a generic type's implementation?

I am facing an issue with the following code snippet, where I am trying to restrict the pairing of Chart objects based on correct types for the data and options objects. However, despite my efforts, the TypeScript compiler is not throwing an error in the s ...

Encountering errors 'LeftSegment' not found and 'infer' not found within the react-router directory in the node_modules folder

Currently, I am in the process of updating my application from react-router v3 to v6. At the moment, I have successfully installed react-router-dom v6.2.1 as well as react-router v6.2. Additionally, since I am using Typescript, I have also installed @types ...

retrieve an object with enum keys that are indexed

I am faced with a situation where I have a collection of interdependent elements, each identified by a unique code (enumeration). My goal is to retrieve the list of elements that depend on a specific element, and then be able to reference them using myElem ...

The variable 'data' is not a property of the type 'any[]'

I am currently facing an issue with a dummy service I created to fetch dummy data. When calling this service from a component ts file, I encountered the following error. After searching through some similar posts, I still haven't been able to resolve ...

Utilize or Bring in an external JavaScript file within Ionic 2

Currently working with Ionic 2 and Typescript Angular 2 and facing an issue. I need to utilize an external JavaScript file located at . How can I import or include this in my project? ...

Is there a way to make the Sweetalert2 alert appear just one time?

Here's my question - can sweetalert2 be set to only appear once per page? So that it remembers if it has already shown the alert. Swal.fire({ title: 'Do you want to save the changes?', showDenyButton: true, showCancelButton: true, ...

What could be causing the primeng dialog to appear blank when conducting Jasmine tests on this Angular TypeScript application?

Having trouble testing a component due to rendering issues? Check out the code snippet below: import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@angular/core'; @Component({ selector: 'app-help', cha ...

Mocking a dependency in Jest that is initialized prior to exporting the module

I'm currently in the process of testing a file that exports a single default function and also needs to create an object prior to exporting the function. It's crucial for this object to remain constant and be the same instance every time the expo ...

Dealing with Alias complications in Typescript/ts-node/tsc

I've got this script in the root directory of my project: // ./root-dir.ts import * as url from "url"; export const rootDir = url.fileURLToPath(new URL(".", import.meta.url)); My tsconfig.json configuration looks like this: { ...

ERROR: Unhandled promise rejection: Unable to find a matching route for URL Segment 'main/knowledge-base'

After setting up dynamic routing for my Angular 6 application, I encountered an error when clicking on a link (for example, 'knowledge base') that stated: core.js:1673 ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segme ...

Is it possible to generate an array of strings from the keys of a type or interface?

Imagine a scenario where we have a type or interface defined as NumberLookupCriteria: type NumberLookupCriteria = { dialCode: string; phoneNumber: string; } or interface NumberLookupCriteria { dialCode: string; phoneNumber: string; } Is there a w ...

Issue with HTTP Interceptor not being effective when making service calls

I've implemented an interceptor to automatically add headers to each HTTP request without manual intervention. However, I'm facing an issue where the service call inside my interceptor is not triggering for some reason. Below is the code snippet: ...

Using AngularJS to inject a service into a static property of a standard class

For my current project, I am combining TypeScript and AngularJS. One of the challenges I'm facing is how to instantiate a static member of a public class (not controller, just a normal class) with a service object. When it comes to controllers, utiliz ...

"Encountering an issue with opening a form in a child component from the parent - receiving an error message 'unable to access

Here's the scenario: I am working with a list of companies that each have an array of projects as one of their variables. The desired functionality is to display the list of companies in the parent component/html, and only open a child component to sh ...

Implementing data waiting strategy in Vue component using TypeScript for rendering

When working with the first component, I encountered a scenario where I needed to open a new page using the router functionality: In Component_1.vue: let route = this.$router.resolve({ name: 'Schedule', params : { id: (this.schedule[0].schedule ...

Determining function return type based on argument type

In the code snippet provided, the function useSearchResults (a React hook) initializes the state based on the argument. The config.state can be any value defined in SearchResultsState or a function that returns a value defined in SearchResultsState. The r ...

Guide on integrating Mapbox GL Draw into an Angular 8 project

I'm currently working on an Angular 8 project that utilizes Webpack. My integration of Mapbox GL JS was successful, however, I am facing issues with importing Mabox GL Draw. Here are the versions I am using: "@angular/core": "8.2.14", "mapbox-gl": "^ ...

Having trouble uploading images using Ionic/Angular to a PHP script

I've been working on incorporating image uploading functionality into my Ionic app. Despite reading multiple tutorials, I haven't been able to get it up and running successfully. I'm specifically aiming for the app to work smoothly in a web ...

The non-null assertion operator seems to be failing to function properly

Error during runtime: Issue with reading property 'id' of an undefined element if (!this.havingMultipleProjects) {//checking for only one project or none at all if (this.authenticationProvider.member.projects != null) this.projectProvider ...