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

What is the correct way to convert a base type value to its extended type in TypeScript?

Consider the TypeScript code block below: type URLEx = URL & { custom: string; }; const url = new URL("http://localhost:3000/foo/var"); const url_x: URLEx = { ...url, custom: "hello", }; console.log(url); // Output properti ...

Changing the child component input in Angular's deep cloning cannot be reflected on the user interface

I am currently working on a parent-child component setup. Within the parent component, I have a BehaviourSubject<SomeObject[]>(). export interface SomeObject(){ field: number; ... editable: boolean } Before passing the object to the child component, ...

combine string inputs when ng-click is triggered

Is there a way to pass a concatenated string using ng-click to MyFunction(param: string)? I have tried but so far, no luck: <input id="MeasurementValue_{{sample.Number}}_{{$index}}" ng-click="Vm.MyFunction('MeasurementValue_{{sample.Number ...

Issue with Angular 17 button click functionality not functioning as expected

Having trouble with a button that should trigger the function fun(). Here's the code snippet I'm using. In my TS file: fun(): void { this.test = 'You are my hero!'; alert('hello') } Here is the respective HTML: &l ...

Navigating through Dynamic URL Parameters with RouterLink in an Angular Application

Within my Angular 2 application, there exists a tab section where users can choose from a collection of separate yet contextually connected components. By clicking on one of these links, the corresponding component is loaded based on the routerLink definit ...

Performing actions simultaneously with Angular 2 directives

My custom directive is designed to prevent a double click on the submit button: import { Directive, Component, OnInit, AfterViewInit, OnChanges, SimpleChanges, HostListener, ElementRef, Input, HostBinding } from '@angular/core'; @Directive({ ...

How should one properly format an array of objects with elements that are different types of variations?

I am currently developing a versatile sorting module using TypeScript. This module will take in an array of data objects, along with a list of keys to sort by. Once the sorting process is complete, the array will be sorted based on the specified keys. To ...

I am seeking guidance for developing my own messaging platform, similar to Discord, using Typescript

Allow me to simplify this for you. This piece of code outlines TypeScript interfaces and namespaces for a WebSocket API that is commonly used in a chat or messaging system. It seems to define the format of messages being exchanged between a client and ser ...

When utilizing a personalized Typescript Declaration File, encountering the error message 'Unable to resolve symbol (...)'

I'm having trouble creating a custom TypeScript declaration file for my JavaScript library. Here is a simplified version of the code: App.ts: /// <reference path="types.d.ts" /> MyMethods.doSomething() // error: Cannot resolve symbol "MyMetho ...

Guide on transforming a Unix timestamp into either "2000-01-01" or "2000-05-24 20:00:00" format, or the opposite way, using Deno

Just starting out with Deno and looking to convert a Unix timestamp such as 1646245390158 into either the format of 2000-01-01 or 2000-05-24 20:00:00, or vice versa. Any tips on how to achieve this? ...

Angular offers pre-determined values that cannot be altered, known as "

I am currently learning Angular and TypeScript, and I came across a task where I need to create an object or something similar that allows me to define a readable but not editable attribute. In Java, I would have achieved this by doing the following: publ ...

Utilizing objects as values with `react-hook-form`

About the Issue I'm facing an issue with a component that utilizes an object as its value. My goal is to integrate this component with react-hook-form The challenge arises when react-hook-form considers my object as a nested form control Background ...

Does combineLatest detach this from an angular service function?

Check out this test service on Stackblitz! It utilizes the combineLatest method inside the constructor to invoke a service method: constructor() { console.log("TEST SERVICE CONSTRUCTED") this.setParameters.bind(this) this.assignFixedParamete ...

Angular not firing slide.bs.carousel or slid.bs.carousel event for Bootstrap carousel

I've searched high and low with no success. I'm attempting to detect when the carousel transitions to a new slide, whether it's automatically or by user click. Despite my numerous attempts, I have been unable to make this event trigger. I ha ...

What is the best way to connect Classes and Objects in Angular5?

Picture a study tool with flashcards and different categories. Each category has a title, while each card contains content, is linked to only one category, and is also connected to another card. How can I establish these connections in Angular 5 and Types ...

Unexpected Issue: Angular 12 Encounters JIT Compiler Unavailability

Lately, I've been encountering a persistent issue with an error message: Uncaught Error: JIT compiler unavailable. Ever since I upgraded from Angular version 8 to 12, whenever I run the ng build --prod --output-path = dist command and build Angular, e ...

What is the correct way to properly parse JSON attributes containing slashes?

I have 2 Custom Interfaces: DataModel.ts export interface Entry{ id: number, content: Note } export interface Note{ message: string } These interfaces are utilized in typing the HttpClient get request to fetch an array of Entries: DataService.ts getE ...

TypeScript in Angular causing lodash tree shaking failure

I am currently working on a large project that involves TypeScript. Various attempts have been made to optimize the use of lodash in my project. Before making any conclusions, I believe it is important to review the outcomes of my efforts. The command I ...

Issue with Material Sort functionality when null objects are present

I've encountered an issue with my code. I created a feature that adds empty rows if there are less than 5 rows, but now the sort function is no longer functioning properly. Strangely, when I remove the for loop responsible for adding empty rows, the s ...

Tips for utilizing the value of object1.property as a property for object2

Within the template of my angular component, I am attempting to accomplish the following: <div> {{object1.some_property.(get value from object2.property and use it here, as it is a property of object1)}} </div> Is there a way to achieve this ...