Working with nested arrays in TypeScript and how to push values onto them

I am facing some challenges with nested array behavior in TypeScript. I am looking for a way to define a single type that can handle arrays of unknown depth.

Let me illustrate my issue:

type PossiblyNested = Array<number> | Array<Array<number>>; // To keep it simple, let's limit it to 2D. In reality, my input could be up to 6-dimensional.

const FLAT_INPUTS: PossiblyNested = [0, 1];
const NESTED_INPUTS: PossiblyNested = [[0], [1]];

const PROCESSED_INPUTS: PossiblyNested = [];

for (let i = 0; i < FLAT_INPUTS.length; ++i) {
    const RAW = FLAT_INPUTS.shift();

    PROCESSED_INPUTS.push(RAW); // Error: Argument of type 'number' is not assignable to parameter of type 'number & number[]'.
}

for (let i = 0; i < NESTED_INPUTS.length; ++i) {
    const RAW = NESTED_INPUTS.shift();

    PROCESSED_INPUTS.push(RAW); // Error: Argument of type 'number[]' is not assignable to parameter of type 'number & number[]'.
}

I have experimented with generic templated types and recursive types, but without success. It seems like I need a clearer understanding of these concepts.

While using 'any' and 'unknown' satisfies TS and Lint, it poses other issues that I want to avoid at all costs.

Although initializing PROCESSED_INPUTS with the expected format works, having to empty it manually before the loop feels cumbersome.

const PROCESSED_INPUTS_FLAT: PossiblyNested = [0]; // include content

PROCESSED_INPUTS_FLAT.pop(); // manual emptying

for (let i = 0; i < FLAT_INPUTS.length; ++i) {
    const RAW = FLAT_INPUTS.shift();
    
    PROCESSED_INPUTS_FLAT.push(RAW); // TS and Lint are happy now
}

Is there a more elegant way to initialize PROCESSED_INPUTS so it can accept dynamically pushed entries, whether they are numbers or arrays of numbers? I thought that was the purpose of my PossiblyNested type... apparently not.

Thank you in advance for any assistance!

Answer №1

Your PossiblyNested type represents either an array containing only numbers, or a two-dimensional array consisting of only numbers. When TypeScript computes the PROCESSED_INPUTS.push, it utilizes intersection types for the contravariant parameters, resulting in the method's type being

(...items: (number & number[])[])
if the array is an Array<number>. This allows push to accept either a single number or an Array<number> if it's an Array<Array<number>>.

To resolve this issue, you can define the type of PROCESSED_INPUTS as

Array<number | Array<number>>
, indicating that it should be an array containing either individual numbers or arrays of numbers.

const PROCESSED_INPUTS: Array<number | Array<number>> = [];

// ...

for (let i = 0; i < FLAT_INPUTS.length; ++i) {
    const RAW = FLAT_INPUTS.shift();

    PROCESSED_INPUTS.push(RAW!); // using non-null assertion because RAW could potentially be undefined
}

Access 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

What is the specific purpose of the 'extend' keyword in Typescript?

Recently, I have been delving into the realm of Javascript/Typescript/React as a PHP developer. During my learning process, I encountered a few perplexing issues that I could not fully grasp. In light of this, I am reaching out to the experienced indiv ...

Another component's Angular event emitter is causing confusion with that of a different component

INTRODUCTION In my project, I have created two components: image-input-single and a test container. The image-input-single component is a "dumb" component that simplifies the process of selecting an image, compressing it, and retrieving its URL. The Type ...

Tips for relocating a switcher button using Ant Design Drawer in a React application

*I'm faced with a challenge in my React project where I need to synchronize the movement of a div (switcher-btn) and an Ant Design Drawer. Whenever the Drawer is opened, I want the switcher-btn to move along with it. Below is the code snippet I'v ...

Is there a way to trigger an image flash by hovering over a button using the mouseover feature in AngularJS2?

When I hover over the 'click me' button, I want an image to appear on the webpage. When I stop hovering, the image should disappear using the mouseover option. This is what I attempted in my app.component.ts and my.component.ts files: Here is t ...

Steps to add annotations to a class descriptor:

Can you help me with the correct way to annotate this piece of code? export class TestCls { static SomeStaticFn(): TestCls { // Do some stuff... // Return the class descriptor for a "fluid usage" of SomeStaticFn return TestCls ...

Issues with NextJS detecting environmental variables

I recently encountered an issue with my NextJS app running on Next.js v12.2.5 where it appears to be ignoring the environment variables I've configured. To address this, I created a .env.local file with the following content: NEXT_PUBLIC_SERVER_URL=h ...

Encountering problems during the installation of Ionic - Error code 'EPROTO'

After attempting to install Ionic on my system, I encountered the following error message: npm ERR! code EPROTO npm ERR! errno EPROTO npm ERR! request to https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz failed, reason: write EPROTO 0:er ...

Encountering null injector errors when running ng test on an Angular application

I have successfully developed a webpage in my Angular application and it is running perfectly. But, when I run ng test, some errors are popping up in Karma like the one shown in the image below: https://i.sstatic.net/lUKS5.png superuser.component.ts // ...

The srcSet functionality in the Image component seems to be malfunctioning in the next.js framework, as it is failing to display

Check out my Next.js code snippet below: import React from "react"; import style from "@/styles/Home.module.css"; import Image from "next/image"; function index() { return ( <> <div className="contai ...

Revamp the button's visual presentation when it is in an active state

Currently, I'm facing a challenge with altering the visual appearance of a button. Specifically, I want to make it resemble an arrow protruding from it, indicating that it is the active button. The button in question is enclosed within a card componen ...

The filtering feature in the Row Group table of PrimeNG controls is malfunctioning and causing issues with the Dropdown

Within my Angular project, I have integrated PrimeNG controls version 11.4.4. Utilizing the Table control, I've structured tabular data to display rows in a grouped fashion with collapsible functionality. I've recently introduced a textbox and d ...

What is the best approach to implement server-side rendering in Next.js while utilizing Apollo React hooks for fetching data from the backend?

I have a Next.js project that is utilizing Apollo GraphQL to retrieve data from the backend. My goal is to render the page using server-side rendering. However, I am encountering an obstacle as the React hooks provided by GraphQL Apollo prevent me from cal ...

Configuring TypeScript for Firefox to recognize specific types such as browser.storage

As per the documentation from Mozilla, I should be able to utilize browser.storage.sync.get in my extension. However, I am encountering some challenges in getting TypeScript to recognize that I can use browser. I have attempted the following (which has wo ...

Can someone explain the meaning of this syntax in a TypeScript interface?

Just the other day, I was trying to incorporate the observer pattern into my Angular 4 application and came across this TypeScript code snippet. Unfortunately, I'm not quite sure what it means. Here's the code: module Patterns.Interfaces { ...

What is the best way to ensure that my mat-slide-toggle only changes when a specific condition is met?

I'm having an issue with a function that toggles a mat-slide-toggle. I need to modify this function to only toggle when the result is false. Currently, it toggles every time, regardless of the result being true or false. I want it to not toggle when t ...

What is the reason behind a TypeScript compiler error being triggered by an 'if-else' statement, while a ternary operator construct that appears identical does not raise any errors?

My function is designed to either return a value of IDBValidKey or something that has been converted to IDBValidKey. When I use the ternary operator to write the function, it works fine. However, if I try to write it as an if-else statement, it causes a co ...

Angular routing functions flawlessly on Chrome Mac but encounters issues on Chrome iOS

Encountering a strange issue. My routing is properly configured and has been verified multiple times. Oddly enough, page1, page3, and page5 are functioning correctly, while page2, page4, and page6 fail to redirect as expected. Upon clicking the redirect ...

Issue with emitting using vuex and vue-socket.io-extended

For practice, I developed a chat app using vue-cli. To facilitate Socket.io connections on the client side, I utilized vue-socket.io-extended as the plugin. In order to connect to the socket within any components, I setup an emit call in vuex store: acti ...

The maximize button mysteriously disappears in Ubuntu when using electron applications

I am encountering an issue with Ubuntu where the maximize screen button is not visible when compiling the Electron project. When I say "compile," I am referring to running electron build => ng build --configuration=dev && electron. My version of Electro ...

Leverage a single attribute from a Typescript interface within another interface

Let's imagine we have a TypeScript Interface like this export interface IMyObj { id: string; type: 'AA' | 'AZ' | 'XY'; ... } Now, I require another interface that includes the same type field export interfa ...