The argument of type 'InputType[]' is incompatible with the parameter of type 'GenericType[]' in Typescript

In this scenario, I am developing a utility function with the objective of dynamically sorting an array of objects (the first parameter) in alphabetical order, based on a specified key passed as the second argument.

The utility function is defined as follows:

interface GenericObject {
  [key: string]: string;
}

export const sortAlphabetically = (array: Array<GenericObject>, sortBy: keyof GenericObject) => {
  let isKeyValid = false;
  
  // check if the key exists in the object before attempting to sort by it
  if (array.length > 0) {
    isKeyValid = array.every(obj => Object.prototype.hasOwnProperty.call(obj, sortBy)  && typeof obj[sortBy] === 'string');
  }

  if (isKeyValid) {
    array.sort((a: GenericObject, b: GenericObject) =>
      a[sortBy].toLowerCase() < b[sortBy].toLowerCase()
        ? -1
        : a[sortBy].toLowerCase() > b[sortBy].toLowerCase()
        ? 1
        : 0,
    );
    return array;
  } else {
    return;
  }
};

At this stage, prior to testing the function, if I attempt to run:

export interface Person {
  name: string;
  surname: string;
} 

const people: Person[] = [
{name: 'John', surname: 'Smith'},
{name: 'Tony', surname: 'Denver'},
{name: 'Mary', surname: 'Howard'},
]

sortAlphabetically(people, 'name');

or this:

export interface Car {
  model: string;
  make: string;
} 

const cars: Car[] = [
{model: 'Golf', make: 'Volkswagen'},
{model: 'X1', make: 'BMW'},
{model: 'Clio', make: 'Renault'},
]

sortAlphabetically(cars, 'make');

An error is encountered:

TS2345: Argument of type 'Person[]' is not assignable to parameter of type 'GenericObject[]'. Type 'Person' is not assignable to type 'GenericObject'. Index signature is missing in type 'Person'.
A similar issue arises for Car[].

As a utility function, it is essential for it to be able to handle any type of object within the array, without triggering type errors. I suspect the issue lies in the way I am defining my GenericObject.

Could someone provide guidance on what I might be overlooking here? Thank you.

Answer №1

To properly implement this, a few challenges need to be addressed.

Your requirements:

  1. Pass an array
  2. Pass a key that belongs to a string property
  3. Sort the array by that key.

To meet the above requirements, here are the steps to follow:

  • Narrow down the key parameter to only allow string properties
    • GenericObject type alone is insufficient for this
    • You need a type-mapper that filters only string properties
  • As there is no direct link between GenericObject and Person types, you cannot cast like this:
    array.sort((a: GenericObject, b: GenericObject)
  • If you generalize the array type to any, this won't work: a[sortBy].toLowerCase() because .toLowerCase() needs to be applicable to any object property type. So a type guard is needed to narrow down that property into GenericObject

In summary:

/* Code snippets for sorting arrays based on specific key in TypeScript */

Visit the Playground Link for more details

Reference: TypeMapper example source

Answer №2

First and foremost, attempting to convert a Person object to a GenericObject is not feasible due to the data structure mismatch. While a Person contains both number and string values, a GenericObject is designed to store only string values.

Additionally, there is a known issue related to the error you encountered. More details can be found in this GitHub link.

It is recommended to replace the interface with a type for better compatibility and functionality.


Check out this TypeScript playground.

type GenericObject = {
  [key: string]: string | number;
}

export const sortAlphabetically = (array: Array<GenericObject>, sortBy: keyof GenericObject) => {
  // Implementation goes here
};

export type Person = {
  name: string;
  id: number;
}; 

const people: Person[] = [
{name: 'John', id: 1},
{name: 'Tony', id: 2},
{name: 'Mary', id: 3},
]

sortAlphabetically(people, 'name');

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

"Import data from a text file and store it as an array of objects using Types

I need assistance with converting the information in my text file into an array of objects. Below is a snippet of the data from the text file: DOCNO NETAMOUNT IREF1 IREF2 DOCDT 001 30000 50 100 6/7/2020 2 40000 40 90 6/7/2020 Currently, t ...

Pipe for Angular that allows for searching full sentences regardless of the order of the words

I am looking to create a search bar that can search for the 'title' from the table below, regardless of the word order in the sentence. I attempted to use a filter pipe to check if the search string exists in the title. I also experimented with ...

Creating a type-safe method wrapper in TypeScript based on function names

Many Q&As discuss creating a function wrapper in TypeScript, but my question is how to do the same with named methods. I am interested in writing something similar to this JavaScript code: function wrap(API, fnName, fn) { const origFn = API.p ...

Implementing Asynchronous context tracking within a Remix application utilizing Express as the server

Utilizing Remix with Express as the server, I aim to develop an Express middleware that establishes an async context to grant all downstream functions (especially those in the "backend" Remix code) access to this context within the scope of a single reques ...

Mastering Typescript Inversify: The Ultimate Guide to Binding Interfaces with Type Parameters

I am trying to figure out how to bind an interface with a type parameter, but I am unsure of the correct way to do it. Here is the Interface: ... export interface ITestHelper<Entity extends ObjectLiteral> { doSomething(builder: SelectQueryBuilder& ...

Issue with displaying entire object using Jest and console.dir

I'm having trouble displaying an error in my Jest test because it's not showing all the levels as expected. import util from 'util' describe('Module', () => { it('should display all levels WITHOUT util', () =& ...

Mastering Two-Way Binding in Angular 2 with JavaScript Date Objects

I am currently utilizing Angular 2 and have encountered the following code: Within the JS file, this code initializes the employee-variable for the template: handleEmployee(employee : Employee){ this.employee = employee; this.employee.sta ...

Tips for ensuring your controls function properly and seamlessly when switching to another page

I utilized the instructions from this post to implement a slider. However, I encountered an issue with the controller when navigating to subsequent pages. While the controller functions correctly on the initial page, it duplicates the same values on the fo ...

Unable to access data from the Array by passing the index as an argument to the method

Having trouble retrieving an item from an Array using method() with an index argument that returns undefined export class DataService { public list = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, ...

Angular does not propagate validation to custom form control ng-select

In my Angular 9 application, I am utilizing Reactive Forms with a Custom Form Control. I have enclosed my ng-select control within the Custom Form Control. However, I am facing an issue with validation. Even though I have set the formControl to be requir ...

Passing a generic type as a parameter in a generic class in TypeScript

TypeScript: I have a method in the DataProvider class called getTableData: public static getTableData<T extends DataObject>(type: { new(): T}): Array<T> { ... } Everything works fine when I use it like this: let speakers = DataProvider.getT ...

Leveraging the 'ref' keyword in TypeScript with Next.js

Currently, I am learning TypeScript in React but encountered a warning. import {useref} from 'react' export default function test(){ cons tmp = useRef() const data = tmp.current?.value return ( <div> <input type = ...

The @Input directive is failing to receive any data from its parent component

Recently, I delved into the workings of Angular's @Input feature and have found it quite useful thus far. My database is Firebase, and the data snippet I am fetching looks like this: { "page_area_business_image" : { "expand" : { ...

Mocking a common function in a shared service using Typescript and Jest

I have a service that is utilized with NestJS, although the issue at hand is not specific to NestJS. Nonetheless, testing in NestJS is involved, and I use it to create the service for testing purposes. This service is responsible for making multiple calls ...

Stringify the keys of JSONObject in a generic way

The current code I have utilizes the Iterator from org.json.JSONObject JSONObject obj = new JSONObject(); obj.put("key1", "value1"); obj.put("key2", "value2"); Iterator keys = obj.keys(); ... However, there is a compile warning present: Iterator is a r ...

What is the process of transforming a basic JavaScript function into a TypeScript function?

As a Java developer diving into TypeScript for frontend development, I've encountered a simple JavaScript code snippet that I'd like to convert to TypeScript. The original JavaScript code is: let numbers = [123, 234, 345, 456, 567]; let names = ...

Converting a string to Time format using JavaScript

I am working with a time format of 2h 34m 22s which I need to parse as 02:34:22. Currently, I am achieving this using the following code: const splitterArray = '2h 34m 22s'.split(' '); let h = '00', m = '00', s = &a ...

When incorporating Typescript into HTML, the text does not appear in bold as expected

Issue with bold functionality in Typescript Hey there, I am currently involved in an Angular project and I came across a problem with a function in a Typescript file that is supposed to bold a specific segment of text formatText() { ......... ...

Troubleshooting problem with TypeScript and finding/filtering operations

let access = environment.access.find(it => it.roleName == userRole); Property 'filter' does not exist on type '{ siteadmin: string[]; manager: string[]; employee: string[]; contractor: any[]; }'. This scenario should work perfectly ...

Is there any distinction between using glob wildcards in the tsconfig.json file when specifying "include" as "src" versus "include" as "src/**/*"?

Is there a distinction between these two entries in the tsconfig.json file? "include": ["src"] "include": ["src/**/*"] Most examples I've come across use the second version, but upon reviewing my repository, ...