The wrong parameter is used to infer the generic function type argument in TypeScript

When using the code snippet below and not explicitly specifying T at function call, such as getOrPut<Item>(...), it is inferred from the create parameter. However, this can lead to the created item type being incompatible with the obj dictionary, as shown in the last line of the code.

function getOrPut<T>(
    obj: { [key: string]: T | undefined },
    key: string,
    create: () => T
): T {
    const value = obj[key];
    if (value) {
        return value;
    } else {
        return obj[key] = create();
    }
};

type Item = { title: string };
type Dictionary = { [key: string]: Item };
const dictionary: Dictionary = {};

// The type of `foo` is {} but an `Item` is expected
const foo = getOrPut(dictionary, 'foo', () => ({}));

Is there a way to enforce the inference of T from the obj parameter?

Playground link.

Answer №1

Yes, it definitely works! Just remember to include an Item parameter in the create section.

function getOrPut<T>(
    obj: { [key: string]: T | undefined },
    key: string,
    create: () => T
): T {
    const value = obj[key];
    if (value) {
        return value;
    } else {
        return obj[key] = create();
    }
};

type Item = { title: string };
type Dictionary = { [key: string]: Item }
const dictionary: Dictionary = {};

// Currently, foo is of type {} but I want it to be of type Item{
const foo = getOrPut(dictionary, 'foo', () => ({} as Item)); // <--- added casting here

Link to Playground

Answer №2

In my search for a solution, I came across a workaround where I specified the return type of create in relation to the type of obj:

function getOrPut<T>(
    obj: { [key: string]: T | undefined },
    key: string,
    create: () => NonNullable<typeof obj[string]>
): T { ... }

It seems that this workaround only functions properly in TypeScript version 3.6.3 and above, whereas my current version is 3.5.3. However, an update should rectify this discrepancy soon. There may be alternative solutions out there that are more effective.

Playgound link.

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 steps should be followed to effectively incorporate Google Fonts into a Material UI custom theme for typography in a React/TypeScript project

Hey there, I'm currently working on incorporating Google fonts into a custom Material UI theme as the global font. However, I'm facing an issue where the font-weight setting is not being applied. It seems to only display the normal weight of 400. ...

AngularJS UI-Router in hybrid mode fails to recognize routes upon initial page load or reload

It appears that when using the @ui-router/angular-hybrid, routes registered within an ng2+ module are not being recognized during the initial load or reload. However, these same routes work fine when accessed by directly typing the URL. I have followed th ...

Solving Typing Problems in React TypeScript with "mui-icons" Props

I have a small compost project with only an App.JSX file that is functioning perfectly, but now I need to convert it to App.TSX. After changing the extension to .TSX, I encountered two errors that I'm unsure how to resolve. function MyComponentWithI ...

I am experiencing issues with the customsort function when trying to sort a column of

Seeking assistance with customizing the sorting function for a Date column in a primeng table. Currently, the column is displaying data formatted as 'hh:mm a' and not sorting correctly (e.g. sorting as 1am, 1pm, 10am, 10pm instead of in chronolog ...

Transitioning create-react-app from JavaScript to Typescript

A few months ago, I began a React project using create-react-app and now I am interested in transitioning the project from JavaScript to TypeScript. I discovered that there is an option to create a React app with TypeScript by using the flag: --scripts-v ...

Leveraging Ionic 2 with Moment JS for Enhanced TimeZones

I am currently working on integrating moment.js with typescript. I have executed the following commands: npm install moment-timezone --save npm install @types/moment @types/moment-timezone --save However, when I use the formattime function, it appears th ...

Why did the compilation of Next.js using TypeScript and ESLint succeed despite encountering errors?

I've been delving into Next.js and encountered unexpected results when integrating TypeScript and ESLint. ESLint seems to work well with TypeScript, but my project compilation is successful despite encountering errors. It's puzzling why the comp ...

Error arises when attempting to pass interface props to a component in a React Typescript application

I am currently delving into the world of React js and typescript. As part of my learning process, I have created a demo application that allows users to input their name and age. The app features an ErrorModal that should pop up on the screen whenever inco ...

Tips for removing data from documents that include automatically generated IDs

In my Angular project, I am utilizing google-cloud-firestore as the database. To import Firestore, I used the code import { AngularFirestore } from '@angular/fire/firestore';. The following function is used to add data to the database: changeLev ...

Tips for efficiently saving data using await in Mongoose

Currently, the code above is functional, but I am interested in utilizing only async/await for better readability. So, my query is: How can I convert cat.save().then(() => console.log('Saved in db')); to utilize await instead? The purpose of ...

Exploring observables for querying the OMDB API and obtaining information on movies

Hey everyone, I'm currently working on implementing a live search feature using Observables in Angular2 to fetch Movie data from the OMDB API. While I can see that it is functioning correctly in the Chrome Network tab, the results aren't showing ...

Is it possible for a component to have multiple templates that can be switched based on a parameter?

Scenario My task is to develop a component that fetches content from a database and displays it on the page. There needs to be two components working together to create a single page, following a specific component tree structure. DataList - receives ful ...

Utilize style as a module in Angular

The issue at hand: Let's take a look at this project structure: /src /public /styles /general /tables.scss /secure /components /someTable1 /someTable.component.ts /someTable.component.css /someTa ...

Is there a way to get interpolation working outside of onInit?

In one component, I have set up a functionality to subscribe to an HTTP GET request from a service and store the response in a variable. The service contains a Subject as an observable so that it can be subscribed to in another component. However, while I ...

How can I bind the ID property of a child component from a parent component in Angular 2 using @Input?

I have a unique requirement in my parent component where I need to generate a child component with a distinct ID, and then pass this ID into the child component. The purpose of passing the unique ID is for the child component to use it within its template. ...

The powerful combination of harp.gl and Angular NG

We've integrated harp.gl into our ng Angular application, but we're encountering issues when trying to connect to data sources that previously worked in our yarn demo. The datasource is created as follows: const dataSource = new OmvDataSour ...

Unable to implement a function from a controller class

I'm currently attempting to organize my Express.js code, but I've hit a snag when trying to utilize a class method from the controller. Here's an example of what my code looks like: product.service.ts export class ProductService { constr ...

Curious about the missing dependencies in React Hook useEffect?

I'm encountering the following issue: Line 25:7: React Hook useEffect has missing dependencies: 'getSingleProductData', 'isProductOnSale', and 'productData'. Either include them or remove the dependency array react-hoo ...

Preventing the compilation process from overwriting process.env variables in webpack: A step-by-step guide

Scenario I am currently working on developing AWS Lambda functions and compiling the code using webpack. After reading various articles, I have come to know that the process.env variables get automatically replaced during compilation. While this feature ...

Evaluate TypeScript method against the interface, for example T['methodName']

Suppose we have an interface: class A { method(x:number) : string } And a function: const b = (x: string) : number; Our goal is to test the function against the interface. Here is one way to achieve this: type InterfaceKey = { method: any }; ...