What is the process of assigning a value type to a generic key type of an object in typescript?

I am currently working on developing a function that can merge and sort pre-sorted arrays into one single sorted array. This function takes an array of arrays containing objects, along with a specified key for comparison purposes. It is important to ensure that the value corresponding to the provided key is always numeric.

Here's the progress I have made so far:

const sortMerge = <
  A extends Array<I>,
  K extends keyof A[number],
  I = A[number] & {
    [key in K]: number;
  },
>(
  arrays: A[],
  key: K,
  sortMethod = SortMethod.asc,
) => {
  const indexesOfArrays = arrays.map(() => 0);

  const mergedSorted = [];

  while (arrays.some((array, i) => array.length > indexesOfArrays[i])) {
    const currentItemsOfArrays = arrays.map(
      (array, arrayIndex) => array[indexesOfArrays[arrayIndex]],
    );
    const comparison = currentItemsOfArrays.map((item) =>
      item ? item[key] : (sortMethod === SortMethod.asc ? Infinity : -Infinity),
    );

    const nextArrayIndex = comparison.indexOf(
      Math[sortMethod === SortMethod.asc ? 'min' : 'max'](...comparison),
    );
    const nextItem = currentItemsOfArrays[nextArrayIndex];

    mergedSorted.push(nextItem);
    indexesOfArrays[nextArrayIndex]++;
  }
  return mergedSorted;
};

While everything seems to be working fine, there is an issue with recognizing I[K] as a numeric type, even though I attempted to define both K and I as generics.

Could you please point out what I may be doing incorrectly?

Anticipated errors/types:

const missingKey = [ { a: 1 } ];
const valid = [ { a: 2, b: 3 } ];
const anotherValid = [ { c: 3, b: 4 } ];

sortMerge([missingKey, valid], 'b') // missingKey[number] is missing property 'b';
sortMerge([valid, anotherValid], 'b') // expected return type: ({ a: number, b: number } | { c: number, b: number })[]

Answer №1

I made some adjustments to the generic constraints:

enum SortOrder {
  asc = 'asc',
}
const mergeSort = <
  Key extends PropertyKey,
  Element1 extends Record<Key, number>,
  Element2 extends Record<Key, number>,

  Array1 extends Element1[],
  Array2 extends Element2[],
  Arrays extends [Array1, Array2]
>(
  arrays: [...Arrays],
  key: Key,
  sortOrder = SortOrder.asc,
): (Arrays[number][number])[] => {
  const arrayIndexes = arrays.map(() => 0);

  const result = [];

  while (arrays.some((array, i) => array.length > arrayIndexes[i])) {
    const currentItems = arrays.map(
      (array, arrayIndex) => array[arrayIndexes[arrayIndex]],
    );
    const comparison = currentItems.map((item) =>
      item ? item[key] : (sortOrder === SortOrder.asc ? Infinity : -Infinity),
    );

    const nextArrayIndex = comparison.indexOf(
      Math[sortOrder === SortOrder.asc ? 'min' : 'max'](...comparison),
    );
    const nextItem = currentItems[nextArrayIndex];

    result.push(nextItem);
    arrayIndexes[nextArrayIndex]++;
  }
  return result;
}

const missingFields = [{ a: 1 }];
const validData = [{ a: 2, b: 3 }];
const anotherValidData = [{ c: 3, b: 4 }];

mergeSort([missingFields, validData], 'b') // missingFields[number] is missing property 'b';
const output = mergeSort([validData, anotherValidData], 'b')

Playground

If you need to infer keys of nested objects or objects in an array, it's best to start from the innermost level.

Key - inferred property of an object

Element - inferred object where keys are Key

Arr - inferred array of Element

Arrays - inferred array of Array

You may have noticed that I began by inferring the bottom-level properties and worked my way up through the arguments.

If you're interested in type inference in function arguments, you can read my article

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

Tips for enabling the TypeScript compiler to locate bokeh's "*.d.ts" files

I recently made the switch from Bokeh's convenient inline extension framework to their npm based out of line build system. I'm currently working on getting my extension to build, but I've noticed that Bokeh organizes all TypeScript *.ts.d fi ...

Is it possible to import the identical file twice consecutively using html and typescript?

I encountered an issue with an input element in my HTML file. Here's what it looks like: <input type="file" (change)="receiveFile($event)" id="inputFileButton" hidden /> This input element is designed for users to import files. Wh ...

Determine whether there are a minimum of two elements in the array that are larger than zero - JavaScript/Typescript

Looking for an efficient way to determine if there are at least two values greater than 0 in an array and return true? Otherwise, return false. Here's a hypothetical but incorrect attempt using the example: const x = [9, 1, 0]; const y = [0, 0, 0]; c ...

Adjusting characteristics in Angular dynamically through JSON

Having trouble changing the value of [icon]="reactAtom" to use a JSON value? Need assistance in updating the [icon] value based on the 'featureItem' received from the parent component. HTML <div> <fa-icon [icon]="reactAtom" class="i ...

Changing a string into an ArrayList of Strings using Java

There is a string that looks like this - "["222.222.222.222", "21.21.21.21"]"; // A plain string that represents a JSON array I need to extract 222.222.222.222 and 21.21.21.21 from the string and store them in an ArrayList. Your help would be greatly ap ...

Retrieving a specific column from a CSV file using PHP

In my PHP code, I have data stored in a csv format. The data looks something like this (please note that it is text with new line characters): $data = 'A,B,C,D,E,F,G,H 1,1,2014-12-10,5,1,2,0,2 2,7,2014-12-09,9,0,,7,2'; I need to figure ...

What is the best way to set the first option in a mat-select to be

My approach to date selection involves using 3 mat-select components for day, month, and year. You can view a demo of this setup here. In an attempt to improve the code, I decided to set the initial options as null by modifying the following lines: allDat ...

React: Implement a feature to execute a function only after the user finishes typing

Currently, I am using react-select with an asynchronous create table and have integrated it into a Netsuite custom page. A issue I am facing is that I would like the getAsyncOptions function to only trigger when the user stops typing. The problem right now ...

How to filter an array in Angular 4 without the need for creating a new array and then displaying the filtered results within the same

In my collection of students, I have their names paired with their academic outcomes. studentResults = [ {name: 'Adam', result : 'Passed'}, {name: 'Alan', result : 'Failed'}, {name : 'Sandy', result : &ap ...

Tips for passing an array between components in Angular 2

My goal is to create a to-do list with multiple components. Initially, I have 2 components and plan to add more later. I will be sharing an array of tasks using the Tache class. Navbar Component import { Component } from '@angular/core'; impor ...

How come a Google Maps API component functions properly even without using *NgIf, but fails to work when excluded in Angular 9?

I recently followed the guide provided in this discussion with success. The method outlined worked perfectly for loading search boxes using this component: map.component.html <input id= 'box2' *ngIf="boxReady" class="controls" type="text" p ...

When I refresh the page in Angular2, the router parameters do not get loaded again

When loading my application on routers without parameters, everything is normal. However, when trying to use a router with params, the application fails to load. For example: localhost:3000/usersid/:id The code for the router is as follows: const appRou ...

Display a popup notification when clicking in Angular 2

Can anyone help me with displaying a popup message when I click on the select button that says "you have selected this event"? I am using Angular 2. <button type="button" class="button event-buttons" [disabled]="!owned" style=""(click)="eventSet()"&g ...

Is it recommended to exclude the NGXS NgxsLoggerPluginModule for production deployments?

When developing, it's common to use the following imports: import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin'; import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin'; Is it recommended to remove these imp ...

Is there a PHP library function that can combine multiple arrays with nested layers into a single array using a specific

I have two arrays that I want to merge based on the "client_id" key (preferably using a PHP function): [all_client] => Array ( [0] => Array ( [client_id] => 1 [client_name ...

Service function in Angular 2 is returning an undefined value

There are two services in my project, namely AuthService and AuthRedirectService. The AuthService utilizes the Http service to fetch simple data {"status": 4} from the server and then returns the status number by calling response.json().status. On the ot ...

Update the nest-cli.json configuration to ensure that non-TypeScript files are included in the dist directory

I've been searching for a solution for hours now: I'm developing an email service using nestJS and nest mailer. Everything was working fine until I tried to include a template in my emails. These templates are hbs files located in src/mail/templ ...

Having difficulty in utilizing localStorage to update the state

I've attempted to log back in using the stored credentials, however it's not working despite trying everything. The dispatch function is functioning properly with the form, but not when accessing localStorage. App.tsx : useEffect(() => { ...

app-root component is not populating properly

As a newcomer to Angular 2, I have embarked on a small project with the following files: app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { MaterialModule } fro ...

Exploring the ng-repeat directive with an array of objects

In the server response, I have an array of objects that I am trying to iterate over using ng-repeat in order to read each value. While trying to use array[0].value works fine, things are not going as expected when using ng-repeat. Despite all my efforts ...