TypeScript Add Extract Kind

I am currently working on implementing a function called sumPluck. This function will allow the user to specify a property of type number from an object in an array, and then calculate the sum of all those properties.

For example:

type A = { prop: number }

const arr: A[] = [{prop: 1}, {prop: 2}];

pluckSum("prop", arr); // 3

I have a basic idea of how to extract the specified property, but I'm having trouble ensuring that the property is definitely a number. Here's what I have so far:

type PropertiesOfTypeNames<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T];
    type PropertiesOfType<T, U> = Pick<T, PropertiesOfTypeNames<T, U>>;

    type NumberProperties<T> = PropertiesOfType<T, number>;

    const pluckSum = <T, K extends keyof NumberProperties<T>>(arr: T[], k: K) =>
        pipe(
            arr,
            R.map(v => v[k]),
            R.sum
        );

An error is being displayed under the map function stating: Type 'T[string]' cannot be assigned to type 'number'

It seems like the mapped type does not properly indicate that v[k] is indeed a number property. I might be missing something in my approach here.

Answer №1

Irrespective of Typescript, I would like to introduce you to an alternative method involving loop fusion, which eliminates redundant array traversal. This technique involves utilizing a unique combinator of type

(b -> c) -> (a -> c -> d) -> a -> b -> d
(written in Hindley-Milner notation) called contramap2nd, as it contra-maps the second argument of a binary function.

The gist of it:

// (b -> c) -> (a -> c -> d) -> a -> b -> d
const contramap2nd = g => f => x => y =>
  f(x) (g(y));

const arrFold = f => init => xs =>
  xs.reduce((acc, x) => f(acc) (x), init);
  
const add = x => y => x + y;
const prop = k => o => o[k];

const pluckSum = k =>
  arrFold(
    contramap2nd(
      prop(k))
        (add)) (0);

console.log(
  pluckSum("foo") ([{foo: 1}, {foo: 2}, {foo: 3}]));

If you prefer, you can manually perform the contra-mapping on the second argument of add, instead of using contramap2nd.

Keep in mind that achieving loop fusion is possible with both map, contramap, and similar techniques.

Answer №2

Managed to figure it out by adding a Record as shown below:

const pluckSum = <T extends Record<K, number>, K extends keyof NumberProperties<T>>(arr: T[], k: K) =>
    pipe(
        arr,
        R.map(v => v[k]),
        R.sum
    );

Update 1 Realized I can simplify it even more by removing the mapped type "NumberProperties" because Record already achieves what I intended. So the final version looks like this:

const pluckSum = <T extends Record<K, number>, K extends keyof T>(k: K, arr: T[]) =>
    pipe(
        arr,
        R.map(v => v[k]),
        R.sum
    );

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

Requesting Next Page via Angular GET Method for Paginated API

Having trouble loading data from a paginated REST API using the code below. Any suggestions for a better approach are welcome! component.ts import { Component, OnInit } from '@angular/core'; import {HttpClient} from '@angular/common/http&a ...

Cache for JSON Schema in VS Code

After updating to TypeScript 1.5 (now out of beta), I am eager to utilize the json schema helpers in VS Code. Upon configuring tsconfig.json, I noticed that only commonjs and amd module options are available, while umd and system are missing based on this ...

`mat chip component in Angular Material is malfunctioning`

Whenever I input a string, it does not display properly within the designated textbox. HTML <mat-form-field class="favorite-fruits"> <mat-label>Favorite Fruits</mat-label> <mat-chip-list #chipList aria- ...

The error with removing the form field control in Angular 7 persists

I am currently in the process of designing a registration page that includes fields for confirming passwords and emails. Users are required to re-enter their password and email address. Below is the structure of the FormGroup: ngOnInit() { this.basicInfo ...

Converting CommonJS default exports into named exports / Unable to load ES module

I've encountered an issue while creating a Discord bot with discord.js using TypeScript. When attempting to run the resulting JavaScript code, I'm facing an error that states: import { Client, FriendlyError, SQLiteProvider } from 'discord.js ...

Console.log is displaying array as [object Object] when utilizing Typescript

When working with an object in typescript called "obj," I encountered a strange behavior. Initially, when I ran the console.log(obj); command, the output in the terminal console was displayed as [object Object]. However, after wrapping it in JSON.stringify ...

The implementation of conditional parameter types in TypeScript

I am struggling with a function where the type of the first argument is determined by the second argument's value of true or false According to my logic, if userExists is true, data should be a string and if it's false, data should be a number. ...

Having difficulty constructing a full stack application using Express

I've been struggling to configure a full stack app using create-react-app with Express and TypeScript. My main issue is figuring out how to compile the server files into a build folder. I have separate tsconfig files for the server and create-react-ap ...

JavaScript - Assigning the same value to 2 properties but console log displays them as distinct values

While iterating through an array of documents, I am encountering a strange issue where setting two properties to the same value results in them having different values when logged using console.log. Here is the code snippet: this.logicItem.$promise.then( ...

Trouble arises when attempting to transfer cookies between server in Fastify and application in Svelte Kit

In the process of developing a web application, I am utilizing Fastify for the backend server and Svelte Kit for the frontend. My current challenge lies in sending cookies from the server to the client effectively. Despite configuring Fastify with the @fas ...

Tips for adding an item to an array within a Map using functional programming in TypeScript/JavaScript

As I embark on my transition from object-oriented programming to functional programming in TypeScript, I am encountering challenges. I am trying to convert imperative TypeScript code into a more functional style, but I'm struggling with the following ...

Encountering type errors in NextJS with TypeScript

I am facing an issue while trying to run this function: "use server"; export const addProduct = async (formData: FormData, imageUrl: string) => { const productName = formData.get("productName")?.toString(); const description = f ...

A guide on effectively utilizing the Map datatype in JavaScript

Recently, I've started delving into the world of es6 Map due to its unique features, but I have some reservations regarding pure operations. For example, when removing properties from objects, I usually use the following function: function cloneOmit ...

Is it possible for a typed function to access object properties recursively?

Is there an efficient method in TypeScript to type a function that can recursively access object properties? The provided example delves two levels deep: function fetchNestedProperty<T, K extends keyof T>(obj: T, prop1: K, prop2: keyof T[K]) { r ...

Challenges with exporting dynamically generated divs using jspdf in an Angular 2 project

I have been utilizing the jspdf library to print div elements in my current project. But I recently discovered an issue where dynamic content within a div is not being printed correctly. Specifically, when incorporating simple Angular if statements, jspdf ...

Creating a JSX.Element as a prop within a TypeScript interface

I need to create an interface for a component that will accept a JSX.Element as a prop. I have been using ReactNode for this purpose, but I am facing issues when trying to display the icon. How can I resolve this issue? export interface firstLevelMenuItem ...

Trouble with Jest when trying to use route alias in Next.js with Typescript

Currently, I am developing a Next.js App (v13.2.3) using Typescript and have set up a path alias in the tsconfig.json. Does anyone know how I can configure the jest environment to recognize this path alias? // tsconfig.json { "compilerOptions": ...

Ionic: Automatically empty input field upon page rendering

I have an input field on my HTML page below: <ion-input type="text" (input)="getid($event.target.value)" autofocus="true" id="get_ticket_id"></ion-input> I would like this input field to be cleared eve ...

Incorporate matTooltip dynamically into text for targeted keywords

I'm currently tackling a challenge in my personal Angular project that utilizes Angular Material. I'm struggling to find a solution for the following issue: For instance, I have a lengthy text passage like this: When you take the Dodge action, ...

Error TS2304: Unable to locate identifier 'RTCErrorEvent'

I am currently working on a WebRTC application using Quasar, typescript and Vue. In my code snippet below, I am encountering an issue where I don't get any errors in WebStorm (as it uses the project's eslint config), and I can navigate to the def ...