A universal function that uses an array parameter to create a custom type

My function takes an array as a parameter and constructs the generic type passed in by the array. Everything is working fine, but I want to add a type check to ensure that the keys of the array match the properties of the generic type, otherwise it should throw a Typescript error.

Interfaces

interface OutputType {
  id: string;
  name: string;
  age: number;
}

interface ArrayType {
  key: string;
  value: any;
}

Generic function

function TestType<T>(array: ArrayType[]): T {
  let newObj = {} as T;

  array.forEach((arrayItem) => {
    newObj[arrayItem.key as keyof T] = arrayItem.value;
  });

  return newObj;
}

This should result in a TS **ERROR** because it's missing the 'id' property

const array = [ 
  {
    key: "name",
    value: "TestName",
  },
  {
    key: "age",
    value: 12,
  },
] as ArrayType[];

This should clear the TS error

const array = [
  {
    key: "id",
    value: "1",
  },
  {
    key: "name",
    value: "TestName",
  },
  {
    key: "age",
    value: 12,
  },
] as ArrayType[];

For the complete code example, visit:

I'm stuck on implementing a custom type for the array parameter.

I want to create a custom type to check the array being passed in and validate it against the properties of the generic type.

Answer №1

This particular problem presents quite the challenge. The solution requires leveraging the generic type T to generate all possible array permutations.

If we have a type T

interface OutputType {
  id: string;
  name: string;
  age: number;
}

the combination of valid arrays would be structured as follows:

type Permutations = [{
    key: "id";
    value: string;
}, {
    key: "name";
    value: string;
}, {
    key: "age";
    value: number;
}] | [{
    key: "id";
    value: string;
}, {
    key: "age";
    value: number;
}, {
    key: "name";
    value: string;
}] | [...] | [...] | [...] | ...

To achieve this union, we utilize a generic type.

type CreatePermutations<
  T extends Record<string, any>, 
  K extends keyof T = keyof T, 
  C extends keyof T = K
> = 
  [K] extends [never]
    ? []
    : K extends K
      ? [{ key: K, value: T[K] }, ...CreatePermutations<T, Exclude<C, K>>]
      : never

We then employ this generic type in our function definition.

function TestType<
  T extends Record<string, any>
>(array: CreatePermutations<T> & ArrayType[]): T {
  let newObj = {} as T;

  array.forEach((arrayItem) => {
    newObj[arrayItem.key as keyof T] = arrayItem.value;
  });

  return newObj;
}

An issue arises when the input provided to the function does not align with the calculated permutations based on the generic type T, resulting in a compile time error.

// Error: Source has 2 element(s) but target requires 3.(2345)
TestType<OutputType>([ 
  {
    key: "name",
    value: "TestName",
  },
  {
    key: "age",
    value: 12,
  },
])

TestType<OutputType>([
  {
    key: "id",
    value: "1",
  },
  {
    key: "name",
    value: "TestName",
  },
  {
    key: "age",
    value: 12,
  },
])

The implementation's drawback lies in its combinatorial complexity. As the object properties increase, calculating all possible combinations becomes resource-intensive.


Playground

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

Property ngIf in Angular is not being supplied by any relevant directive within the embedded template

When attempting to use ngIf, I encountered an error with a red underline. The error message states: "Property ngIf is not provided by any applicable directive on an embedded template." I then attempted to import commonModel, but received a new error: "src ...

Template literal types in TypeScript and Visual Studio Code: An unbeatable duo

I'm encountering an issue while attempting to utilize literal types in Visual Studio Code. When following the example from the documentation, https://i.stack.imgur.com/V6njl.png eslint is flagging an error in the code (Parsing error: Type expected.e ...

Is it possible to utilize a variable within the 'has-text()' function during playwright testing?

With Playwright, I am attempting to locate an element based on the value of a variable For instance: let username = 'Sully' await page.click(`li:has-text(${username})`) However, I encounter the following error: page.click: Error: "has-tex ...

Incorporating TypeScript with jQuery for efficient AJAX operations

I recently added jQuery typings to my TypeScript project. I am able to type $.ajax(...) without encountering any compile errors in VS Code. However, when I test it on localhost, I receive an error stating that "$ is not defined." In an attempt to address t ...

Step-by-step guide on mocking a method within a method using JEST

Currently, I am facing a challenge in unit testing a method named search. This method consists of three additional methods - buildSearchParameter, isUnknownFields, and readAll. I am looking to mock these methods but I am uncertain about the process. Can ...

Using Typescript to intersect and define default props in React components

https://i.sstatic.net/Rw3e9.png Is it viable to utilize intersection in order to avoid explicitly listing every type of defaultProps, but rather derive it from a declared variable? My code example shows that even though myProp is declared as nullable, it ...

Connecting Ag Grid with modules

Unable to link with modules as it's not a recognized attribute of ag-grid-angular https://i.sstatic.net/2zwY2.png <ag-grid-angular #agGrid style="width: 100%; height: 100%;" id="myGrid" class="ag-theme-balham" [mod ...

What is the best way to ensure that the base class Resolver finishes before allowing the derived class Resolver to execute?

I have a situation where many of my resolvers (@angular/router Resolve) need to query the same data before executing their route-specific queries. To streamline this process, I want to create a resolver base class that resolves the initial data before the ...

Transforming JSON into object instances with Angular

I am facing an issue in my Angular application where I need to convert a JSON object into an array. Although the mapping process is successful, the data within the array does not retain the properties and methods of my original object class. This hinders m ...

Having trouble transferring data using HttpClient in Angular 4 with a Symfony backend

Resolved using NelmioCorsBundle. I am currently utilizing angular 4.3.3 for the front end and Symfony3 for the backend, with the addition of FosRestBundle. When working with Angular, I primarily use HttpClient to handle data request and transmission. Da ...

Leveraging the power of the Async pipe within an Angular TypeScript file

Using the async pipe in HTML involves utilizing the syntax "Products$ | async as products". But can we also access these same products in the TypeScript file? Is this possible? ...

Determine the data type of an object's key

I have a XInterface defined as: export interface XInterface { foo: (() => Foo[]) | Foo[], bar: string, baz: number } When declaring an object using this interface, I want the type of foo to be Foo[], like so: const myObj: XInterface = { ...

Error encountered in TypeScript's Map class

When working with TypeScript, I keep encountering an error message that reads "cannot find name Map." var myMap = new Map(); var keyString = "a string", keyObj = {}, keyFunc = function () {}; // assigning values to keys myMap.set(keyString, "val ...

Displaying updated information in Angular

I recently developed a chat application using Angular that utilizes the stomp socket from @stomp/ng2-stompjs. To display all messages, I am leveraging *ngFor. <p *ngFor="let item of messages" style="padding: 5px; font-size: 18px"> <span style ...

Unhandled rejection error occurred when attempting to redirect to a different page in Next.js version 13, resulting in a NEXT_REDIRECT error

Currently, I'm attempting to validate whether a user is logged in and if not, redirect them to the login page. Here's the code snippet I am using: import { onAuthStateChanged } from 'firebase/auth' import { auth } from '../src/fir ...

Begin the NextJS project by redirecting the user to the Auth0 page without delay

I am new to coding and currently working on a project using Typescript/NextJS with Auth0 integration. The current setup navigates users to a page with a login button that redirects them to the Auth0 authentication page. However, this extra step is unneces ...

Having trouble establishing a connection with the C# Controller when processing the frontend request

Having trouble implementing the Search by siteId functionality using typescript and C#. The issue arises when trying to connect to the C# controller from the frontend request. The parameter I need to pass is siteId. Below is the code snippet: HTML: ...

Recoil is unable to assign a string type alias to a string in typescript

export type OrderOption = | '-createdAt' | 'participationFee'; export const orderState = atom<OrderOption>({ key: 'order', default: '-createdAt', }); interface OrderListProps { options: { name: stri ...

Encountering ExpressionChangedAfterItHasBeenCheckedError in Angular 17 even after invoking detectChanges method

I'm encountering a minor problem with Angular and its change detection mechanism. I have created a simple form where additional input fields can be added dynamically. However, every time I click the add button, an ExpressionChangedAfterItHasBeenChecke ...

What's preventing me from using just one comparison condition in TypeScript?

The issue at hand is quite simple: An error occurred because I tried to compare a number with a 'Ref<number>' object. It seems ridiculous that I can't compare two numbers, but as I am new to Typescript, I would greatly appreciate some ...