Can you explain the correct method for assigning types when destructuring the `callbackFn.currentValue` in conjunction with the `.reduce()` method? Thank you

I'm working with an array of arrays, for example:

const input = [['A', 'X'], ['B', 'Y'],...];

In addition to that, I have two enums:

  enum MyMove {
    Rock = 'X',
    Paper = 'Y',
    Scissors = 'Z',
  };

  enum OpponentMove {
    Rock = 'A',
    Paper = 'B',
    Scissors = 'C',
  };

I am trying to use Array.prototype.reduce() on my input array, but I'm struggling with annotating the callbackFn.currentValue. This is what I have so far:

  const score: number = input.reduce(
    (acc: number, ([opponentMove, myMove]: [OpponentMove, MyMove])) => {

(referenced from Types when destructuring arrays), but I keep getting these errors:

02/index.ts:38:9 - error TS2322: Type 'string[]' is not assignable to type 'number'.

38   const score: number = input.reduce(
           ~~~~~
02/index.ts:39:21 - error TS2304: Cannot find name 'opponentMove'.

39     (acc: number, ([opponentMove, myMove]: [OpponentMove, MyMove])) => {
                       ~~~~~~~~~~~~
02/index.ts:39:35 - error TS2304: Cannot find name 'myMove'.

39     (acc: number, ([opponentMove, myMove]: [OpponentMove, MyMove])) => {
                                     ~~~~~~
02/index.ts:39:44 - error TS2554: Expected 1-2 arguments, but got 3.

39     (acc: number, ([opponentMove, myMove]: [OpponentMove, MyMove])) => {
                                              ~~~~~~~~~~~~~~~~~~~~~~

What is the correct way to annotate [opponentMove, myMove] in this case?

Answer №1

Instead of specifying the types for callback parameters in your destructuring assignment, it is recommended to properly declare the type of the input variable from the beginning. For example:

const input: Array<[OpponentMove, MyMove]> = [
  [OpponentMove.Rock, MyMove.Rock],
  [OpponentMove.Paper, MyMove.Paper]
];

Consequently, the callback parameters destructured when using reduce() will be inferred based on context:

const score: number = input.reduce((
  acc, [opponentMove, myMove]) => 2, 0);
// (parameter) opponentMove: OpponentMove
// (parameter) myMove: MyMove

This results in the desired outcome.


Please note that I adjusted the initialization of input compared to your original version to prevent issues:

const input: Array<[OpponentMove, MyMove]> = 
  [['A', 'X'], ['B', 'Y']]; // error!

When working with an enum, it's important to refer to values using the enum itself. While MyMove.Rock and "X" may have the same value at runtime, TypeScript treats MyMove.Rock as a distinct version of "X" and raises errors if you use the latter incorrectly:

const oops: MyMove = "X"; // error! Type '"X"' is not assignable to type 'MyMove'.
const okay: MyMove = MyMove.Rock; // okay

Although you could utilize a type assertion to "cast" one type to another, it is risky:

const assert = "X" as MyMove; // okay
const whaa = "x" as MyMove; // compiler accepts this but it's a mistake

Therefore, following the proper approach is key:

const input: Array<[OpponentMove, MyMove]> = [
  [OpponentMove.Rock, MyMove.Rock],
  [OpponentMove.Paper, MyMove.Paper]
];

This aligns with the intended behavior of enums. If not satisfied, consider utilizing plain objects and literal types as they offer similar functionality:

const MyMove = {
  Rock: 'X',
  Paper: 'Y',
  Scissors: 'Z',
} as const;
type MyMove = typeof MyMove[keyof typeof MyMove];
// type MyMove = "X" | "Y" | "Z"

const OpponentMove = {
  Rock: 'A',
  Paper: 'B',
  Scissors: 'C',
} as const;
type OpponentMove = typeof OpponentMove[keyof typeof OpponentMove];
// type OpponentMove = "A" | "B" | "C"  

With this setup, either way of referencing the values will function correctly since the type of MyMove.Rock is simply the literal type "X" without any enum-specific subtype considerations:

const input: Array<[OpponentMove, MyMove]> = [
  ["A", "X"], ["B", "Y"]
]; // okay

const input: Array<[OpponentMove, MyMove]> = [
  [OpponentMove.Rock, MyMove.Rock],
  [OpponentMove.Paper, MyMove.Paper]
]; // still acceptable

Link to code playground

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

How to Utilize an Array from Observable Object in Angular 2 with ngFor and Async Pipe

I am currently learning about the utilization of Observables in Angular 2. Here is a snippet of code from a service I am working with: import {Injectable, EventEmitter, ViewChild} from '@angular/core'; import {Observable} from "rxjs/Observab ...

Tips for incorporating a mail button to share html content within an Angular framework

We are in the process of developing a unique Angular application and have integrated the share-buttons component for users to easily share their referral codes. However, we have encountered an issue with the email button not being able to send HTML content ...

Create a function that retrieves the value associated with a specific path within an object

I want to implement a basic utility function that can extract a specific path from an object like the following: interface Human { address: { city: { name: string; } } } const human: Human = { address: { city: { name: "Town"}}}; getIn< ...

Tips for dynamically incorporating filtered selections into a Mat-Select dropdown

I am seeking guidance on how to prevent changing the values of already selected values in other rows when each row of the formArray is altered. Adding controls dynamically and correctly retrieving values in filters are functioning properly. The issue arise ...

When using Angular, the Zone can be accessed through VSCode intelligence, but it appears as undefined when running in

Upon attempting to utilize Zone within the ngOnInit function in my Angular application, I placed a breakpoint on line 11. However, when inspecting the function Zone(parent, zoneSpec){....}, it returns undefined upon launching in Chrome browser. Here is the ...

What is the best way to retrieve a {collection object} from a JavaScript map?

My application utilizes a third-party library that returns the map in the following format: public sids: Map<SocketId, Set<Room>> = new Map(); When I try to access it using the code below: io.of("/").adapter.sids.forEach(function(va ...

Using Typescript for AngularJS bindings with ng.IComponentController

Currently, I am utilizing webpack alongside Babel and Typescript Presently, the controller in question is as follows: // HelloWorldController.ts class HelloWorldController implements ng.IComponentController { constructor(private $scope: ng.IScope) { } ...

What sets apart exporting a component from importing its module in another module?

When working with Angular, consider having both module A and module B. If I intend to utilize "A-component" within components of module B, what is the distinction between importing module A in Module B compared to including the "A-component" in the exports ...

Creating Meta tags for Dynamic Routes in a Nuxt 3 Build

I recently encountered an issue when trying to implement dynamic OpenGraph meta tags on a dynamically generated route in Nuxt 3 (and Vue 3). I attempted to set the meta tags dynamically using Javascript, as it was the only dynamic option supported by Nuxt ...

The Typescript error message states that the type '{ onClick: () => void; }' cannot be assigned to the type 'IntrinsicAttributes'

I'm a beginner in Typescript and I'm encountering difficulties comprehending why my code isn't functioning properly. My goal is to create a carousel image gallery using React and Typescript. However, I'm facing issues when attempting t ...

Uh-oh! There seems to be an issue with the response. It appears that the

Struggling to navigate the graphql in apollo client/server for a Next.js project. The terminal is showing an error message No HTTP methods exported in '..\app\api\graphql\route.ts'. Export a named export for each HTTP method.. ...

Exploring the power of TypeScript for authenticating sessions with NextJS

Utilizing next-auth's getSession function in API routes looks something like this for me: const mySession = await getSession({ req }); I have confirmed that the type of the mySession is outlined as follows: type SessionType = { user: { email: s ...

When trying to style a Material UI component in Mui v5, no matches for overloads were found

In my attempt to enhance the style of a Material UI Component (TextField) shown in the image, I encountered an error that seems to persist no matter what troubleshooting steps I take. Interestingly enough, I never faced such issues when working with styled ...

What is a way to execute a series of requests using rxjs similar to forkJoin and combineLatest, without needing to wait for all requests to finish before viewing the results?

Consider you have a list of web addresses: urls: string[] You create a set of requests (in this instance, utilizing Angular's HTTPClient.get which gives back an Observable) const requests = urls.map((url, index) => this.http.get<Film>(url) ...

Issue encountered when attempting to save items in the browser's local storage

I'm encountering an issue: ERROR Error: Uncaught (in promise): DataCloneError: Failed to execute 'put' on 'IDBObjectStore': Position object could not be cloned. Error: Failed to execute 'put' on 'IDBObjectStore& ...

"Encountering an issue with mounting components in React Unit Testing with Jest and Typescript

Having developed a simple app with components, here is the code: import GraphicCanvas from './Graphing/GraphCanvas'; import { drawCircle } from './Graphing/DrawCircle'; function App() { return ( <div className="App"&g ...

What is the importance of moving prop types into a type or interface in React components?

Consider the scenario where I have a functional component: const MyText = ({ value }) => ( <div className="my-fancy-text">{value}</div> ); Now, when utilizing Typescript, I face the need to introduce typing. A straightforward ...

Exploring the functionality of two-way data binding in Angular - a beginner's guide

Transitioning from a different framework and switching from JavaScript to Angular & TypeScript has left me feeling confused about how to efficiently share data/values between components. ...

Angular2: Promise Rejection: Quotes cannot be used for evaluation in this component

I'm currently working on a component in Angular that includes an input parameter: import {Component, Input} from '@angular/core'; @Component({ selector: 'comment', template: ` <div class="col-lg-6 col-md-6 ...

What steps can be taken when encountering TS errors regarding missing required fields that are in the process of being filled?

In a typical scenario, the process involves creating an empty object and populating it with the necessary data. Initially, this object does not contain any properties, which leads to TypeScript flagging an error since it lacks the required type. For insta ...