A guide to using TypeScript to type object rest spread for all keys within a function

There is a function that takes an object as input and returns an object as output. This function adds a key to the incoming object and returns the modified object. The object's structure is not known beforehand, but it must contain two specific keys.

const myFunction = ({
  num1,
  num2,
  ...rest
}: {
  num1: number;
  num2: number;
}) => ({
  num1,
  num2,
  sum: num1 + num2,
  ...rest,
});

myFunction({ num1: 4, num2: 3, foo: 'bar' });
// or myFunction({ num1: 4, num2: 3, baz: 'qux', quux: 'quuz' });

In TypeScript, an error is reported for the 'foo' key.

Argument of type '{ num1: number; num2: number; foo: string; }' is not assignable to parameter of type '{ num1: number; num2: number; }'.
  Object literal may only specify known properties, and 'foo' does not exist in type '{ num1: number; num2: number; }

The above was a simplified example. Now, let's look at a more complex function and an attempt to handle it using 'extends'.

import type { NextApiRequest, NextApiResponse } from 'next';
import { getSession } from 'utils/sessions';

const withAuthentication = async <
  T extends {
    request: NextApiRequest;
    response: NextApiResponse;
  },
  K extends T
>({
  request,
  response,
  ...rest
}: T): Promise<
  {
    userSession: {
      issuer: string;
      publicAddress: string;
      email: string;
    };
  } & K
> => {
  const userSession = await getSession(request);

  return { request, response, userSession, ...rest };
};

export default withAuthentication;

The actual error message for this function is as follows.

Type '{ request: NextApiRequest; response: NextApiResponse<any>; userSession: any; } & Omit<T, "request" | "response">' is not assignable to type '{ userSession: { issuer: string; publicAddress: string; email: string; }; } & K'.
  Type '{ request: NextApiRequest; response: NextApiResponse<any>; userSession: any; } & Omit<T, "request" | "response">' is not assignable to type 'K'.
    '{ request: NextApiRequest; response: NextApiResponse<any>; userSession: any; } & Omit<T, "request" | "response">' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint '{ request: NextApiRequest; response: NextApiResponse<any>; }'.

How would you define the type for such a function?

Answer №1

Utilize generics for more flexibility.

Check out the demo: https://repl.it/@chvolkmann/InternalFrugalCopyleft

interface Arguments {
  a: number
  b: number
}

const performAction = <A extends Arguments>(args: A) => ({
  ...args,
  sum: args.a + args.b
})

console.log(performAction({ a: 10, b: 5, foo: 'bar' }))
// Expected Result:
// { a: 10, b: 5, foo: 'bar', sum: 15 }

Answer №2

Using the rest parameter in destructuring can present some challenges when trying to typecheck, but by simply spreading the argument object and including the userSession property, you can arrive at a relatively clear solution:

const withAuthentication = async <
  T extends {
    request: NextApiRequest;
    response: NextApiResponse;
  }
>(arg: T): Promise<{
    userSession: {
      issuer: string;
      publicAddress: string;
      email: string;
    };
  } & T> => {
  const userSession = await getSession(arg.request);
  return { ...arg, userSession };
};

(TypeScript playground)

Answer №3

This block of code successfully compiles, however, I am uncertain if it is the most optimal approach to the problem at hand.

import { UserSession } from 'features/user-authentication/types';
import type { NextApiRequest, NextApiResponse } from 'next';
import { getSession } from 'utils/sessions';

const withAuthentication = async <
  T extends {
    request: NextApiRequest;
    response: NextApiResponse;
  }
>({
  request,
  response,
  ...rest
}: T): Promise<
  {
    request: NextApiRequest;
    response: NextApiResponse;
    userSession: UserSession;
  } & Omit<T, 'request' | 'response'>
> => {
  const userSession = await getSession(request);

  if (userSession) {
    return { request, response, userSession, ...rest };
  }

  throw new Error('Unauthenticated');
};

export default withAuthentication;

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

After upgrading from Angular 13 to 14, the <app-root> component is failing to load. Despite no noticeable changes, the issue persists

Having upgraded my Angular 13 app to version 14, I encountered an issue where the page no longer loads properly. Despite checking the configuration and stripping down the index.html file to its basics, the issue persists - nothing seems to be working. Upo ...

Obtaining a customized variation of a class identified by a decorator

I am working with a class that is defined as follows: class Person { @OneToOne() pet: Animal; } Is there a method to obtain a transformed type that appears like this? (Including {propertyKey}Id: string to properties through the use of a decorator) ...

Encountering a problem where I am unable to input text in a textbox after making changes

I'm having trouble editing a text field. I can't seem to type anything when trying to edit. Strangely, everything works fine when adding a user, but the issue only arises during editing. Take a look at my code below - const initialState = { ...

Problem encountered when attempting to save log information to a file using typescript-logging in Angular 11

Seeking insight on how to log information, debugging details, and error messages into a file (such as app.log or error.log) using typescript-logging for Angular. Alternatively, is there a more efficient method to log debug/info/errors in Angular 11? I have ...

Tips for successfully typing the backtick character when transitioning to Typescript:

I am currently working on a Typescript Vue project involving Leaflet. I came across some code for lazy-loading map markers, but it was written in Javascript. Although the code works fine, I keep receiving errors and warnings from VSCode because this is not ...

Tips on how child component can detect when the object passed from parent component has been updated in Angular

In the child component, I am receiving an object from the parent component that looks like this: { attribute: 'aaaa', attribute2: [ { value }, { value }, { value }, ] } This object is passed to th ...

The issue of session type not updating in Next.js 14 with Next-auth 5 (or possibly version 4) is a common concern that needs to

Experimenting with new tools, I encountered an issue when trying to utilize the auth() function to access user data stored within it. TypeScript is indicating that the user does not exist in Session even though I have declared it. Here is my auth.ts file: ...

What is the process of creating a callback in Angular using TypeScript?

Despite finding numerous resources, I am still struggling to fully grasp the concept at hand. The issue revolves around two functions in particular: roulette_animation(){ do animation (may take 5 sec) } alertResult(){ alert('You win') } My obje ...

Observable subscription does not result in updating the value

One of the challenges I'm currently facing in my Angular application is the synchronization of data from a server. To keep track of when the last synchronization took place, I have implemented a function to retrieve this information: fetchLastSyncDate ...

The module has defined the component locally, however, it has not been made available for

I have developed a collaborative module where I declared and exported the necessary component for use in other modules. import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DateslideCompone ...

Error occurred during the Uglify process: Unable to access the 'kind' property as it is undefined

I developed a project using TypeScript (version 3.9.3) and Node (version 10.16.3), but now I want to minify the code by converting it to JavaScript and running UglifyJS. However, after going through this process, the services that were functioning properly ...

Resolving the Error: "Type 'Customer | undefined' is not compatible with type 'Customer'" in Angular

I encountered an issue with the following code: ... export class ListCustomersComponent implements OnInit { customers: Array<Customer> = []; showCustomer?: Customer; isSelected: boolean = false; deletedCustomer?: Customer; returnedMessa ...

Implement Cross-Origin Resource Sharing in Angular frontend

I am facing an issue with two microfrontends running on different ports (4200 and 4201) where one frontend is unable to access the translation files of the other due to CORS restrictions. To overcome this obstacle, I created a custom loader in my code that ...

Issue with running gulp ser on first attempt in SPFX

Every time I try running gulp serve, I encounter the following issue: Error: Unable to locate module '@rushstack/module-minifier-plugin' Please assist me with this problem. Thank you! ...

What could be causing input to be blocked in certain situations while using my Angular directive with compile function?

Recently, I created a directive that adds a class based on a certain condition. You can find the code snippet at the end of this question. The directive functions as expected in a simple use case where it's applied to a required field: <input typ ...

Error: Disappearing textarea textContent in HTML/TS occurs when creating a new textarea or clicking a button

I've encountered an issue with my HTML page that consists of several textareas. I have a function in place to dynamically add additional textareas using document.getElementById("textAreas").innerHTML += '<textarea class="textArea"></text ...

Error: Unable to parse string in URI within vscode API

Console LOG displays: \Users\skhan\Library\Application Support\Code\User\summary.txt The loop is used to replace the slashes. It works fine in Windows but not in Ubuntu and Mac. This is an example on OSX 10.11.6. Howev ...

Retrieving child elements from parent identifiers using Typescript

I've been working on creating a new array with children from the data fetched from my database. While my initial attempt was somewhat successful, I believe there are some missing pieces. Could you assist me with this? Here is the raw data retrieved f ...

What is the best way to pass a state within a route component in react-router?

... import { useNavigate, NavigateFunction } from "react-router"; ... function Form(): JSX.Element { const navigateToCountry = (country: string) => { // Code to navigate to country page with the given country } const [selectedCount ...

Can the return type of a function be determined dynamically at runtime by analyzing a function parameter value?

Let's delve into this concept with an illustrative example! interface Query { foo: any; } interface Mutation { bar: any; } type OperationName = keyof Query | keyof Mutation; const request = async <T>(args, operationName: OperationName): P ...