Is it possible to attach "traits" to a current array of objects using TypeScript?

I have a variety of object types that I need to manipulate within an array consisting of those object types.

type AB = { a:number, b:number}
type CD = { c:number, d:string}
type DE = { d:number, e:boolean}

let state: AB[] = []
function onStateChange(newState: AB[]) {
  console.log(newState)
  state = newState
}

function add(blank: AB ) {
  onStateChange([
    ...state,
    blank,
  ]);
}

function remove(index: number) {
  onStateChange([...state.slice(0, index), ...state.slice(index + 1)]);
}

function update(index: number, updated: Partial<AB>) {
  onStateChange([...state.slice(0, index),
  { ...state[index], ...updated},
   ...state.slice(index + 1)]);
}

Playground

To achieve this goal with the add, remove, and update methods applied to state, I am exploring ways to include additional methods that result in modified copies of the original data

interface WithListManagement<T> {
  withAdd(blank: T ) : T[] {
    return [
      ...state,
      blank,
    ]);
  }
  withRemove(index: number) {
    return [...state.slice(0, index), ...state.slice(index + 1)]);
  }
  withUpdate(index: number, updated: Partial<AB>) {
    return [...state.slice(0, index),
      { ...state[index], ...updated},
      ...state.slice(index + 1)]);
  }
}

Thus, something like this approach can be implemented:

let state: WithListManagement<CD> = []
function add(blank: CD ) {
  onStateChange(state.withAdd({c:1, d:'foo'});
}

Note that my objective is to avoid altering the Array prototype as I specifically intend for these modifications to be targeted towards specific lists rather than all arrays.

UPDATE regarding context.

The primary aim is to create functionality akin to default interface methods in Java while also considering type erasure in TypeScript.

For instance, if we take the type AB[], which inherently possesses standard Array methods such as map and length, along with functionalities like JSON.stringify().

I envision having an interface or potentially a class that provides capabilities similar to WithListManagement<AB>, possessing the same behavior as AB[] but inclusive of the supplementary methods outlined above.

Answer №1

Implementing functions in an interface is not considered valid because interfaces are essentially types that get erased during compilation.

An example of a valid implementation for the interface is:

interface WithListManagement<T> {
  withAdd(blank: T): T[]
  withRemove(index: number): T[]
  withUpdate(index: number, updated: Partial<AB>): T[]
}

declare var someState:WithListManagement<number>

someState.withAdd(42) // works as expected

However, it's noted that the above implementation using methods can be risky due to them being bivariant. It's recommended to use arrow functions instead:

interface WithListManagement<T> {
  withAdd: (blank: T) => T[]
  withRemove: (index: number) => T[]
  withUpdate: (index: number, updated: Partial<AB>) => T[]
}

For more specific details on what you aim to achieve, additional context would be helpful.

UPDATE To extend the built-in Array class:

type AB = { a: number, b: number }

class WithListManagement<T> extends Array<T>{

    withAdd(blank: T) {
        return [
            ...this,
            blank,
        ];
    }
    withRemove(index: number) {
        return [...this.slice(0, index), ...this.slice(index + 1)];
    }
    withUpdate(index: number, updated: Partial<AB>) {
        return [...this.slice(0, index),
        { ...this[index], ...updated },
        ...this.slice(index + 1)];
    }
}

const state = new WithListManagement<AB>();

state.push({ a: 1, b: 2 }) // works as intended

//[{ a: 1, b: 2 }, { a: 3, b: 4 }]
const result = state.withAdd({ a: 3, b: 4 }) // success

type AB = { a: number, b: number } class WithListManagement<T> extends Object { [Symbol.iterator]() { return this; } next() { return { value: 42 } } withAdd(blank: T) { return [ ...this, blank, ]; } } const state = new WithListManagement<AB>(); state.withAdd // successful execution

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

The parameter 'string | JwtPayload' cannot be assigned to the parameter 'string'

Utilizing Typescript alongside Express and JWT for Bearer Authorization presents a specific challenge. In this situation, I am developing the authorize middleware with JWT as specified and attempting to extricate the current user from the JWT token. Sampl ...

Is it possible to assign default values to optional properties in JavaScript?

Here is an example to consider: interface Parameters { label: string; quantity?: number; } const defaultSettings = { label: 'Example', quantity: 10, }; function setup({ label, quantity }: Parameters = { ...defaultSettings }) { ...

Angular API snapshot error: The type 'IJobs' does not match the expected type 'IJobs[]'

Currently, I am in the process of learning and attempting to construct a job board using Angular 10. Although my API setup seems to be functioning properly, when navigating to the job detail page on Chrome, an error is displayed: ERROR in src/app/job-det ...

Issue with Angular: ngForm object does not capture selected option

Revise to clean up unnecessary code. Having trouble displaying the selected option when I print the form object to the console. It's showing as undefined. Any guidance on what might be wrong with this code would be appreciated. Let me know if more in ...

What is the reason for a boolean extracted from a union type showing that it is not equivalent to true?

I'm facing a general understanding issue with this problem. While it seems to stem from material-ui, I suspect it's actually more of a typescript issue in general. Despite my attempts, I couldn't replicate the problem with my own types, so I ...

Resolving conflicts between AbortSignal in node_modules/@types/node/globals.d.ts and node_modules/typescript/lib/lib.dom.d.ts within an Angular project

An issue occurred in the file node_modules/@types/node/globals.d.ts at line 72. The error message is TS2403: Subsequent variable declarations must have the same type. Variable 'AbortSignal' should be of type '{ new (): AbortSignal; prototype ...

Creating a custom button for exporting a high chart to CSV

My Angular project involves exporting a chart to various formats, such as png, jpeg, pdf, and SVG. However, I am encountering an issue when trying to export the chart as CSV or . I have attempted the following code: this.lineChart.chart.downloadCSV(); //F ...

Having trouble with Visual Studio 2015 not compiling TypeScript within an ASP.NET Core project?

Seeking assistance with my Angular app development setup in VS2015. Even though it is recognized as a TypeScript Virtual Project, I am facing issues getting the transpiled files into the wwwroot folder within my ASP.NET Core project. Here's an overvie ...

Retrieve all items that match the ids in the array from the database

I'm having trouble receiving a list of items that match with my array of ids. Here's a snippet from the Angular component code: this.orderService.getSpecyficOrders(ids) .subscribe(orders => { ... Where ids is an array of [{_id : ID }, ...

What could be causing my date variable to reset unexpectedly within my map function?

Currently, I'm utilizing a tutorial to create a custom JavaScript calendar and integrating it into a React project You can find the functional JavaScript version in this jsfiddle import { useState, useRef, useMemo } from 'react' import type ...

typescript - instantiate an object using values stored in an array

Assume we have a model defined as follows. export interface Basicdata { materialnumber: number; type: string; materialclass: string; } We also have an array containing values that correspond directly to the Basicdata model in order, like this: ...

Avoid using unnecessary generic types while updating a TypeScript interface on DefinitelyTyped, especially when using DTSLint

After attempting to utilize a specific library (query-string), I realized that the 'parse' function was returning an any type. To address this, I decided to update the type definitions to include a generic. As a result, I forked the DefinitelyTy ...

Utilizing Ionic 2 with Typescript for executing forEach operations

I am in the process of migrating my AngularJS application to Angular 2. In my AngularJS controller, I had a JSON array that I was iterating through to display data in an accordion list. Now, I need to implement the same functionality in my Angular 2 compon ...

Button in Angular gets stuck when a touchscreen is long pressed

In my Angular2 application, I am facing an issue with a button when running on a Windows 10 touchscreen PC in Chrome. Normally, the button works fine and executes the click function. However, if the button is held for 1-2 seconds, it gets stuck and fails t ...

Implementing a GIF loader in your webpack configuration for a Typescript/React/Next.js application

Upon inserting a .gif file in my Typescript React app, an error message has surfaced. ./src/gif/moving.gif 1:6 Module parse failed: Unexpected token (1:6) You may need an appropriate loader to handle this file type, currently no loaders are configured to p ...

Using Next.js and TypeScript to Send Props to Dynamically Typed Objects

I am in the process of developing an application using Next.js with TypeScript. I have encountered an error message stating Type 'VoidFunctionComponent<ShirtDetailProps>' is missing the following properties when passing props to a component ...

Angular dependency issue: Expected '{' or ';' for @types/node

I encountered an error while running "ng serve" in my Angular application. Originally built as Angular 2, it was upgraded to Angular 8 (with attempts at versions 6 and 7 along the way). However, after migrating from Angular 5, I started experiencing errors ...

What is the best way to reverse a text using typescript?

Is there a way to convert text into an inverted form using TypeScript? Regular text: how are you? Expected text: https://i.sstatic.net/8GnsU.png ...

Tips for utilizing Provide/Inject in Vue.js while leveraging TypeScript

I am currently working with Vue.js and TypeScript along with the vue-property-decorator package. The documentation suggests that I can achieve something like this: import { Component, Inject, Provide, Vue } from 'vue-property-decorator' const s ...

Tips for effectively jasmine testing with the createSpyObj function, where class properties are defined as spies

When attempting to create a mock service with set-only properties, I encountered errors indicating that the value was undefined despite following the guidance in the documentation here. I want to be able to track the values of these properties during test ...