Using Typescript to extract/calculate types with limitations without the need to explicitly extend or broaden them

I have a function called build that constructs a User object using the provided parameters. I want to define the function in such a way that it recognizes which parameters are being passed and incorporates them into the return value.

Initially, I thought of capturing the type of params and utilizing it for the return type:

type User = { name: string; admin: boolean };

const build = <T extends User>(params: T): T => params

This approach works effectively since now I can use it like this:

build({ name: 'Jan', admin: true });
// the return type is: { name: string, admin: true } as intended (noting admin: true, not boolean)

However, by capturing the type with the <T extends User> constraint, it unintentionally widens the type. Now, the following scenario is allowed without any errors:

build({ name: 'Jan', admin: true, foo: 'shouldnt be allowed' });
// the return type becomes: { name: string, admin: true, foo: string }

If I opt for a simpler approach where I don't capture the type and just return a User, then I do get the expected behavior where passing parameters that do not exist in User leads to an error, but the function only returns a User without additional information from the parameters:

const build = (params: User): User => params

// Type error as expected: Object literal may only specify known 
// properties, and 'foo' does not exist in type 'User'
build({ name: 'Jan', admin: true, foo: 'sdf' })

My query is whether there is a method to capture the type of params for usage in the return type without causing the type to widen unnecessarily.

Answer №1

Although Typescript does not have built-in support for exact types, you can set up custom constraints on argument types. Take a look at this example in the playground:

type User = { name: string; admin: boolean };

const build = <T extends User>(
    params: keyof T extends keyof User ? T : never
): T => params

// const user1: { name: string; admin: true; }
const user1 = build({ name: 'Jan', admin: true, });

// ERROR
const user2 = build({ name: 'Jan', admin: true, x: 1});

Expanding on the same concept, here's a slightly more detailed approach with improved error handling:

const build = <T extends User>(
    params: keyof T extends keyof User ? T : User
): Pick<T, keyof User> => params

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

Create and save data to a local file using Angular service

I am facing an issue with my Angular service: import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { person } from '../interfaces/iperson ...

Determine the type and create an instance of a fresh class

In my app, I have a function that handles all API requests. Any interaction I make goes through this function. I'm trying to set a specific return type for this function, but the return type is of a class. In order to use the methods of this class, I ...

Disallow the use of properties in a nested interface

Is there a way to define an interface or type that restricts a specific key in a child of the interface when used in union types? I am looking for the correct definition for Abc: type Abc = { someField: { prohibited?: never, }, }; type Use ...

Guide to importing a function from a Javascript module without declaration

Currently, I am utilizing a third-party Javascript library that includes Typescript type declarations in the form of .d.ts files. Unfortunately, as is often the case, these type declarations are inaccurate. Specifically, they lack a crucial function which ...

Leveraging TypeScript to share information between directives in AngularJS through asynchronous calls

Although I've found some scattered information on how to tackle this issue, I haven't been able to find a solid solution. In my AngularJS application, I have an asynchronous call that fetches data from a server and I need to store it in a variab ...

The declaration file for the module 'tailwind-scrollbar' could not be located

Currently, I am in the process of utilizing Tailwind packages for a Next.js application, however, I have encountered an issue that has proved to be quite challenging to resolve. Every time I attempt to add a "require" statement to my tailwind.config.js fil ...

Guide on navigating to a specific page with ngx-bootstrap pagination

Is there a way to navigate to a specific page using ngx-bootstrap pagination by entering the page number into an input field? Check out this code snippet: ***Template:*** <div class="row"> <div class="col-xs-12 col-12"> ...

Testing a function that involves multiple HTTP requests through unit testing

I am struggling with writing a unit test for a function that indirectly triggers multiple HTTP requests. The service I am testing has the following structure: /* content.service.ts */ import { Injectable } from "@angular/core" import { ApiService } from ...

Tips for managing numerous HTTP requests in Angular 6

I have a method that is trying to chain together 3 requests like this: showProfileDetails() { this.getUserInfo(this.currentUser.id).pipe( mergeMap(e => this.getAccounts(this.currentUser.id) ), mergeMap(e => this.getPayments ...

Combining Vue with Typescript and rollup for a powerful development stack

Currently, I am in the process of bundling a Vue component library using TypeScript and vue-property-decorator. The library consists of multiple Vue components and a plugin class imported from a separate file: import FormularioForm from '@/FormularioF ...

Is there a more efficient approach to displaying a list of elements and sharing state in React with TypeScript?

Check out this code sample I'm attempting to display a list with multiple elements and incorporate a counter on the main element that updates every time one of the buttons is clicked. I'm uncertain if this approach is optimal, as I am transition ...

Encountering the error message "Received interpolation ({{}}) when an expression was expected" is a common issue when trying to interpolate tag attribute values within an ngFor loop

I've been working with a Devextreme's Datatable and my goal is to dynamically configure the table using a columns configuration array. The plan is to loop through this array using an ngFor and set column tags properties dynamically. Now, the bi ...

Angular Ahead-of-Time (AOT) compilation causes multiple route definitions to be

Having a bit of trouble configuring ahead-of-time compilation for my lazy-loaded Angular app. The lazy-loaded routes are specified in the app.routes.ts file, which is imported by app.module.ts. Running ngc results in the content of app.routes.ts being mer ...

Every time I attempt to destructure the state object in react typescript, I encounter the error message stating 'Object is possibly undefined'

Whenever I attempt to destructure my state object in react typescript, I encounter an error stating Object is possibly 'undefined'. When I try using optional chaining, a different error pops up saying const newUser: NewUser | undefined Argument o ...

Tips for utilizing 'safe-json-stringify' within Angular 4 Application

I have a specific requirement to convert a JSON object to a string. However, simply using JSON.stringify() does not work due to circular references. After some research online, I came across a package that can handle this issue for me. The only problem is ...

Showcase pictures within an angular smart table

Is it possible to display images in a column within an ng smart table? We have several columns consisting mostly of data, with one column dedicated to displaying images. Following the ng smart table concept, I attempted to implement the code below which cu ...

Focusing on the active element in Typescript

I am working on a section marked with the class 'concert-landing-synopsis' and I need to add a class to a different element when this section comes into focus during scrolling. Despite exploring various solutions, the focused variable always seem ...

Why isn't the constraint satisfied by this recursive map type in Typescript?

type CustomRecursiveMap< X extends Record<string, unknown>, Y extends Record<string, unknown> > = { [M in keyof X]: M extends keyof Y ? X[M] extends Record<string, unknown> ? Y[M] extends Record<st ...

Mastering the Conversion from NGRX Effect to NGRX Effect v15

I am currently working on converting the code snippet to NGRX 15. As a newcomer to Angular, I could use some guidance. "@ngrx/effects": "^15.4.0" @Injectable() export class SnackbarEffects { @Effect({ dispatch: false }) cl ...

Efficient Searching with Typescript and Lodash: Boosting Performance with Arrays and Objects

I am faced with the challenge of converting between two classes called MyObject and MyObjectJSON, which have helper methods to assist in the conversion process: myObj.toJSON() and MyObject.fromJSON(). Currently, I have instances of these classes represent ...