Sort elements based on an array of specified keys

Picture a scenario where you have an interface like this:

interface Person {
    name: string;
    age: number;
    height: number;
}

Now, imagine having a function that accepts an array of keys from the interface and returns an object with only those specified keys.

const person: Person = {
   name: 'John Doe',
   age: 30,
   height: 175,
}
const getPartialPerson = (keys: keyof Person[]) => {
   return Object.keys(person).reduce((acc, val) => {
       if keys.includes(val) {
          acc[val] = person[val]
       }
       return acc;
   }, {});
}
const partialPerson = getPartialPerson(['name', 'age']);

partialPerson.name; // should be valid
partialPerson.age; // should be valid
partialPerson.height; // ERROR: does not exist in type

The goal is to allow the type to automatically determine the valid properties based on the input array.

Although it seems achievable, I am struggling to make it happen.

One possible approach could involve something like this:

const getPartialPerson = <T extends keyof Person>(keys: keyof T[]): Pick<Person, T> => {
   ....
}

Answer №1

Your suggested type signature for function getPartialHuman appears to be accurate. The issue lies in your utilization of keyof, which is incorrect. keyof T[] does not signify (keyof T)[]; rather, it denotes keyof (T[]), meaning 'the keys of T[]', and since T[] is an array, its keys are numbers.

Below is the functioning code as per your initial implementation (playground):

interface Human {
    name: string;
    age: number;
    height: number;
}

const human: Human = {
   name: 'test',
   age: 101,
   height: 104,
}

const getPartialHuman = <T extends keyof Human>(keys: T[]): Pick<Human, T> => {
   return Object.keys(human).reduce((acc: Partial<Human>, val) => {
       if (keys.includes(val as T)) {
          acc[val as T] = human[val as T];
       }
       return acc;
   }, {}) as Pick<Human, T>;
}

const partialHuman = getPartialHuman(["name", "age"]) // Pick<Human, "name" | "age">

This implementation seems a bit convoluted. Alternatively, here's my revised version (playground):

const getPartialHuman = <T extends keyof Human>(keys: T[]): Pick<Human, T> => {
   return Object.fromEntries(keys.map((key) => [key, human[key]])) as Pick<Human, T>
}

Answer №2

Let's consider the scenario where you have a class named Car:

class Car {
  public constructor(
    public wheels: number,
    public doors: number,
    public size: string
  ) {}

  getCarProperties<T extends keyof Car>(...keys: T[]): Pick<Car, T> {
    const selectedProperties: Pick<Car, T> = {} as Pick<Car, T>;

    keys.forEach((key) => {
      selectedProperties[key] = car[key];
    });

    return selectedProperties;
  }
}

Now, let's create an instance of the Car class:

const car: Car = new Car(2, 4, 'medium');
console.log(car); // { wheels: 2, doors: 4, size: 'medium' }

We can then use the method that takes specific properties as input and generates a partial object:

const subCar = car.getCarProperties('wheels', 'doors');
console.log(subCar); // { wheels: 4, doors: 2 }

Try this code snippet

Explanation:

The method accepts an array of keys as its parameter.

An important aspect is the line:

getCarProperties<T extends keyof Car>(...keys: T[])

In this part, T is defined as extending from the keys of Car, ensuring accuracy. By passing the keys as arguments instead of an array, the method becomes more user-friendly.

The objective is to return an object containing only those specified keys.

Once the keys are collected, a new object is created with targeted properties copied over from the original object. The type of this new object should be something like: Pick<Car, T>

Hopefully, this sheds some light on the concept!

Edit: It's worth noting that the types themselves validate whether the keys are valid or not, alerting your IDE if necessary. Nevertheless, if dealing with DTO or external input for keys, incorporating a check like if (car.hasOwnProperty(key)) within the forEach loop may be beneficial.

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

Looking to effortlessly move and arrange items with ng2-drag-drop in Angular 2?

I have encountered a problem with the ng2-drag-drop library. I am trying to drag and drop items from one div to another, and then freely move the dropped item within the droppable area. One issue I'm facing is that when I drop my draggable item in th ...

Exporting an angular component as a module

I have successfully created a widget using Angular elements that works well in HTML. However, I am unsure of how to export it as a module for use in other Angular, React, Vue web applications. I want to be able to import import { acmaModule } from package- ...

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 advisable to implement preset-react alongside preset-typescript when the jsx option is configured for 'react'?

Utilizing babel along with preset-typescript for transpiling react tsx files. According to the documentation, when the jsx option is configured as react, preset-typescript will convert <div /> to React.createElement("div") and generate a ...

The Array Find method in TypeScript does not always provide the accurate result

I am working with a list of data containing various attributes like latitude, longitude, title, and pair status. [0: {lat: "37.829998", lng: "-122.003152", title: "Allocate", pairStatus: 1, …} 1: {lat: "37.829998", lng: "-122.003152", title: "Commissio ...

Transforming a base64 string into a uint8Array or Blob within Typescript/Angular8

I'm new to handling Base64 encoded strings, uint8Array, and Blobs. I've integrated a pdf viewer library from this repository https://github.com/intbot/ng2-pdfjs-viewer into our Angular 8 web application. I'm facing an issue where I am sendin ...

Understanding the export and export default behavior in JavaScript

I am in the process of developing a NodeJS and Express application and I require a configuration module that executes only once during startup and then provides a serverConfig object to any other modules that may need these values. This is what I have so f ...

Encountered a Building Error: "The type .... is included in the declarations of two modules: AppModule and Ionic."

As I strive to generate my APK for Android, I executed the following command: ionic cordova run android --prod --release Version of Ionic being used: Ionic V3 My app currently does not employ lazy loading (I confess I am not even sure how to go about th ...

I need help with querying the date format in Mongoose and MongoDB. The specific date I am trying to query is stored in the field "ticketTimeStartDate" and is in the format: "Mon Oct 18 2021 12:28:59 GMT+0000 (

Can anyone provide guidance on querying this specific date format in moongose and MongoDB? The date field is as follows: "ticketTimeStartDate": "Mon Oct 18 2021 12:28:59 GMT+0000 (Coordinated Universal Time)", ...

Troubleshooting the issue of conditional extension in Typescript for "Array or Object" not functioning as anticipated

My goal is to create a TypeScript type generic that has the following structure: type APIDataShape<T extends { id: unknown } | Array<{ id: unknown }>> = T extends Array<any> ? Array<{ id: T[number]["id"]; ...

The React Fabric TextField feature switches to a read-only mode once the value property is included

I've been grappling with how to manage value changes in React Fabric TextFields. Each time I set the value property, the component goes into read-only mode. When utilizing the defaultValue property, everything functions correctly, but I require this i ...

Enhance your React application by addressing and fixing minor issues

Whenever I input npm start while compiling the application to see changes, I consistently encounter minor errors such as: - Instead of <...ComponentName />, change it to ComponentName/> and similar instances to get rid of unnecessary spaces. I n ...

The process of extracting a value from an array of objects encountered an error due to the undefined object

I am looking to extract the value from an array within an object while also implementing error checking. The code I currently have checks if a specific key exists in the object and if the value associated with that key is of type array. If both condition ...

Using media files (mp4 and mp3) from a USB drive within a software application

I am currently developing an application that allows users to watch movies and listen to songs. The frontend is being built with Angular, while the backend uses Python/Flask. Once completed, this application will be running on a Raspberry Pi 4. To store t ...

How to configure VSCode for automatically transpiling TypeScript and launching NodeJS debugger with just one press of the F5 key?

Working with VSCode+TypeScript+NodeJS can feel overly complex compared to a C# project in Visual Studio. Just hitting F5 in C# compiles the project and initiates debugging. How can I configure VSCode to do the same for TypeScript+NodeJS? What am I missing ...

The error message "Cannot find property 'email' in type '{setup():{email : Ref<string>; password: Ref<string> }}'" is being displayed

Currently experimenting with vue js 3 and typescript in the composition API while utilizing firebase for authentication <template> <input placeholder="Password" @input="this.password"/> </template> <script lan ...

What is the expected return type in TypeScript of a function that returns a void function?

I recently received feedback during a code review suggesting that I add return type values to my functions. However, I am unsure of what return type to assign to this particular function: function mysteryTypeFunction(): mysteryType { return function() ...

Issue with Cypress: The 'each' property is missing on type 'TestFunction'

We recently implemented cypress 9.3.1 into our project for end-to-end testing. However, we are encountering an issue where our existing jest tests are not compiling in the CI pipeline. All parameterized tests are showing the following error: Property &apo ...

Running a TypeScript program that has been compiled with module resolution is not working as expected

I am currently in the process of compiling a TypeScript file with the following code: import { magic } from 'lib/magic'; magic(); The file structure looks like this: ./src/ main.ts lib/ a/magic.ts b/magic.ts Within ...

Can you explain the concept of "Import trace for requested module" and provide instructions on how to resolve any issues that

Hello everyone, I am new to this site so please forgive me if my question is not perfect. My app was working fine without any issues until today when I tried to run npm run dev. I didn't make any changes, just ran the command and received a strange er ...