Utilize generics to define the data type of the output

Within my Angular service, there is a method that retrieves data from Sync Storage:

 getFromSyncStorage(key: string): Promise<Object | LastErrorType> {
    return new Promise(function (resolve, reject) {
      chrome.storage.sync.get(key, function (v: Object) {
        if (chrome.runtime.lastError) {
          return reject(chrome.runtime.lastError);
        }
        resolve(v && v[key]);
      });
    });
  }

It's important to note that LastErrorType is defined as:

export type LastErrorType = typeof chrome.runtime.lastError;

The challenge arises when predicting the return type based on the key passed. For instance:

getAllRunHistory() {
    return this.cds.getFromSyncStorage('my-special-key');
}

Attempting to specify the return type as an array results in a TypeScript error:

getAllRunHistory() : Promise<Array<any>>{
    return this.cds.getFromSyncStorage('my-special-key');
}

How can generics be utilized to properly type this versatile getFromSyncStorage method?

Answer №1

To achieve this functionality, you can utilize generics. Define a type that represents the mapping from key to value type. For your specific case, it may look something like this:

type DataStorageMap = {
    "special-key": Array<any>,
    [k: string]: Object // all other keys
}

Subsequently, you can define your method with the following signature:

fetchData<K extends keyof DataStorageMap>(
  key: K
): Promise<DataStorageMap[K] | ErrorType> {
   // implementation
}

Keep in mind that you might need to use type assertions within the method's implementation if the compiler cannot confirm that "special-key" corresponds to Array<any>.

When you call the method like this:

retrieveData() {
    return this.database.fetchData('special-key');
}

The return type will be recognized as

Promise<Array<any> | ErrorType>
. This is the desired behavior, unless you are certain that you will never encounter an ErrorType, in which case you can adjust the type definition like so:

type DataStorageMap = {
    "special-key": Array<any>, // no error expected
    [k: string]: Object | ErrorType // potential error scenario
}

fetchData<K extends keyof DataStorageMap>(
  key: K
): Promise<DataStorageMap[K]> {
   // implementation
}

I hope this explanation is helpful to you. Best of luck with your implementation!

Answer №2

It seems like utilizing generics is the optimal solution. The original method has been modified to the following:

 getFromSyncStorage<T>(key: string): Promise<T> {
    return new Promise(function (resolve, reject) {
      chrome.storage.sync.get(key, function (v) {
        if (chrome.runtime.lastError) {
          return reject(chrome.runtime.lastError);
        }
        resolve(v && v[key]);
      });
    });
  }

Subsequently, we can implement it in this manner:

this.getFromLocalStorage<Array<MyType>>(mds.key).then(...)

However, a drawback is that I am unsure how to specify the error type for the promise, which is unfortunate. I believe it's a limitation within the Promise TypeScript typings :(

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

typescript: best practices for typing key and value parameters in the forEach loop of Object.entries()

I have a specific object with key/value pairs that I need to iterate over using the entries() method of Object followed by a forEach() method of Array. However, I'm struggling to understand how to avoid a typescript error in this situation: type objTy ...

Converting Javascript tools into Typescript

I'm currently in the process of migrating my Ionic1 project to Ionic2, and it's been quite an interesting journey so far! One challenge that I'm facing is how to transfer a lengthy list of utility functions written in JavaScript, like CmToFe ...

Accelerated repository uses TypeScript to compile a node application with dependencies managed within a shared workspace

Struggling to set up an express api within a pnpm turborepo workspace. The api relies on @my/shared as a dependency, which is a local workspace package. I have been facing challenges in getting the build process right. It seems like I need to build the s ...

What is the best way to create a generic function parameter for a single property of an object?

I am trying to refactor a generic function into accepting parameters as a single object function test<T>(a: string, b: T, c: number) Instead, I want the function to receive an object like this: function test(params: {a: string; b: T, c: number}) I ...

Using the TypeScript compiler API to determine the location in the generated code of a particular AST node

I am aiming to retrieve the specific TypeScript AST node's location (start and end) in the emitted JavaScript file. Consider this code snippet: const program = ts.createProgram(tsconfig.fileNames, tsconfig.options); const aNode = program.getSourceFi ...

Is it necessary to conceal Angular navigation controls when the user is not authenticated?

In Angular, is there a standardized method for hiding controls when the user is not logged in? We already have the CanActivate guard which checks if a user can access a route. Would it be better to hide the route initially if the user is not logged in or l ...

Setting up VSCode to run various tasks

My TypeScript project in Visual Studio Code has a specific task outlined as follows: { "version": "0.1.0", // The command is tsc. "command": "tsc", // Show the output window only if unrecognized errors occur. "showOutput": "silent", // Und ...

Tips on rotating a material-ui icon

Having trouble rotating a material-ui icon using the CSS animation property. Can anyone assist in identifying what may be causing the issue? Link to example code sandbox I'm looking for a continuously rotating icon. ...

Combining Promises in Typescript to create a single Promise

Is there a way for me to return the temp_data object filled with data after using .map? Currently, it always returns undefined because I initialize temp_data as an empty object. But if I don't do this, I can't use LooseObject. Can anyone suggest ...

What is the best way to include a Web Service within an export variable in Angular 2 using TypeScript?

Is there a way to incorporate JSON data retrieved from the server into the export var HEROES: Hero[ ] function? Here is the link: https://angular.io/resources/live-examples/toh-5/ts/eplnkr.html In app/mock-heroes.ts, you will find the following data, im ...

Issue with the declaration of custom types in Typescript

I've created a type declaration for r-dom as shown below: /// <reference types="react" /> declare module 'r-dom' { interface IRDOMFacade extends React.ReactDOM { (component: React.Component<any, any>, properties?: ...

Customizing AxiosRequestConfig with Axios in TypeScript can greatly enhance the functionality and

Currently working with React and Axios. Lately, I've been experimenting with custom configurations in Axios as shown below: import $axios from 'helpers/axiosInstance' $axios.get('/customers', { handlerEnabled: false }) However, wh ...

Is there a way to override the JSON.stringify method within the JSON class of a TypeScript project without using a custom call?

Dealing with a React Native and TypeScript app here. I keep encountering an error from Fabric every week: "JSON.stringify cannot serialize cyclic structures." The frustrating part is that the error seems to pop up randomly, without any specific scenario tr ...

The array remains undefined even after being assigned within the subscribe function

I have encountered an issue in my Angular app where the array productLocations is being assigned in the ngOnInit method within a subscription, but it remains undefined when used in another method. Despite following advice on Stackoverflow to move the assig ...

Encountering Error 404 while submitting a form on Prisma, Axios, and NestJS

Currently, I am working on a Sign Up page using SolidJs and NestJS with Prisma. However, when I try to submit the form, I encounter an error that says POST 404 (Not Found) and this error is also returned by axios. Additionally, my setup includes postgres ...

How can I transfer data between methods within Angular?

Help Needed: I have a service file with two methods, each having its own API. I want to call the getData method within the deleteData method. Can anyone guide me on how to achieve this? .service.file getData(): Promise<PagedResult<Sites>> { ...

SonarQube alerting you to "Eliminate this unnecessary casting"

Can someone help me understand why SonarQube is flagging this error and suggest a resolution? The unnecessary cast should be removed. Promise.all([ this.customerViewCmr4tProvider.getData(activeNumber), this.customerBillManagementProvider.getData(ind ...

How can you inject the parent component into a directive in Angular 2, but only if it actually exists?

I have developed a select-all feature for my custom table component. I want to offer users of my directive two different ways to instantiate it: 1: <my-custom-table> <input type="checkbox" my-select-all-directive/> </my-custom-table> ...

Can we determine the data type of a value within a class instance by utilizing a function to retrieve it?

Is it feasible to create a function that maintains typing and functions in the same way as this: class Example { someNumber:number = 1; someString:string = "test"; } const example = new Example(); const value = example.someNumber; // typ ...

The code below is not working as it should be to redirect to the home page after logging in using Angular. Follow these steps to troubleshoot and properly

When looking at this snippet of code: this.router.navigate(['/login'],{queryParams:{returnUrl:state.url}}); An error is displayed stating that "Property 'url' does not exist on type '(name: string, styles: AnimationStyleMetadata". ...