Determining the correct type for a recursive function

I have created a function that is capable of recursively traversing through a nested or non-nested object to search for a specific key and extract its value.

const findName = <T extends object>(obj: T, keyToFind: string): T[] => {
   return Object.entries(obj)
      .reduce((acc, [key, value]) => {
         if(key === keyToFind) {
            return acc.concat(value)
         } else {
            //is the next level of nesting an object so we can keep recursively searching for the key?
            if (typeof value === 'object') {
               return acc.concat(findName(value, keyToFind)) //problem is here because value is of type any
            } else {
               return acc
            }
         }
      }, [])
}

I am currently facing an issue where I need to define the type of value. When calling findName recursively, I encounter the following error:

No overload matches this call.
  Overload 1 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.
    Argument of type 'any[]' is not assignable to parameter of type 'ConcatArray<never>'.
      The types returned by 'slice(...)' are incompatible between these types.
        Type 'any[]' is not assignable to type 'never[]'.
          Type 'any' is not assignable to type 'never'.
  Overload 2 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.
    Argument of type 'any[]' is not assignable to parameter of type 'ConcatArray<never>'.

I am seeking guidance on how to define a type to resolve this compiler error.

Here is the link to the Playground with the current situation.

Thank you

UPDATE

I have managed to resolve the error by setting reduce's generic to type T[], but value is still tagged as a type of any which is causing linting issues. Is there a way to specify it as a string when concatenating?

const findName = <T extends object>(obj: T, keyToFind: string): T[] => {
   return Object.entries(obj)
      .reduce<T[]>((acc, [key, value]) => {
         if(key === keyToFind) {
            return acc.concat(value)
         } else {
            //is the next level of nesting an object so we can keep recursively searching for the key?
            if (typeof value === 'object') {
               return acc.concat(findName(value, keyToFind))
            } else {
               return acc
            }
         }
      }, [])
}

Answer №1

When utilizing reduce, it is essential to specify the type of the initializer, especially if it is an empty data structure. Otherwise, TypeScript will restrict you from populating it, as the accumulator and the initializer must match in type.

I leave it to you to determine the appropriate type, but consider starting with asserting [] as any[] to make the error disappear.

Here's a piece of advice: opt for push over concat in this scenario if possible. By using push, you are appending to a new array, ensuring the reduce expression remains pure with no observable side effects, thus enhancing efficiency.

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

Learn how to dynamically show the chosen option from a dropdown menu in input text fields using AngularJS

html file: <select ng-model="shipping" ng-options="shipping.shipping for shipping in shipAddress"> <option value=''>--Select Address --</option> </select> <form name="shippingForm" class="form-horizontal" role="form" ...

The Vuex commit has exceeded the maximum calstack size limit

Currently facing an issue https://i.sstatic.net/l1WWH.png The error seems to be isolated to this specific section mounted() { this.$nextTick(() => { let ctx = this.$refs.canvas.getContext('2d') let { chartType, dataOptions ...

retrieving information from server via ajax to display on chart

I am currently utilizing the jqPlot JavaScript library for generating graphs and charts in one of my applications, which can be found at . Within my application, there are approximately 5-6 pages where I have integrated this library. However, I would like ...

Reduce the size of the header in the Sydney theme for WordPress as you scroll

Currently, I am attempting to reduce the size of the header once scrolling down. My focus is on refining the child theme. Displayed below is a screenshot illustrating the appearance of the header at the top of the page: https://i.stack.imgur.com/wojGf.pn ...

Transmit data to PHP servers

When the button in my HTML is clicked, it should trigger a query in a PHP file that will display the results. However, the button does not seem to be working as expected. What could be causing this issue? Here is the code I have tried: <?php $reply_ ...

Make sure all of the inner tags are properly contained within their respective containers

In my explanatory bubble, I aim to include text within the explain-header div as a container for my explanations: /*Explain Bubble*/ .explain-container { position: relative; overflow: hidden; max-width: 70vw; max-height: 50vh; background-c ...

Encountering a proxy-related error in an Ionic App triggers a 500 error response

I have a device that is embedded and I am attempting to establish a connection with it through my app using http. In order to achieve this, I am utilizing Ionic Proxy. Within my project file, I have the following configuration: { "name": "MyApp", "ap ...

Stop the default behavior of a link based on the Ajax response

A controller has been created in Magento to check if there are any products in a list. If products exist in the list, it will return true; otherwise, it will return false. The front-end below triggers an ajax call. It must remain a link and cannot be chan ...

Ramjet: Unveiling the Magic of Making Elements Appear and Disappear

Currently, I am attempting to implement the code for ramjet from . However, I am facing an issue where element a does not disappear when transitioning into b. Additionally, I am encountering an error message "--Uncaught TypeError: Cannot read property &apo ...

The Chartjs bar graph fails to display data upon initial page load

My HTML page includes a bar chart created using the ChartJS library. However, upon loading the page, the chart appears empty without displaying the data I provided and without rendering the bars. It only responds after clicking on the legend - first displa ...

Embracing the Quirks of JSON: A Call for a

Currently, I am in the process of developing a webpage that involves incorporating four distinct JSON entities (objects, arrays). Excuse my lack of appropriate jargon. Upon receiving the JSON data, it is structured as an object with numerous sub-objects, ...

Incorporate the Input() component into your codebase and take advantage of its dot notation features, such as

Many Angular directives utilize dot notation options: style.padding.px style.padding.% attr.src In addition, libraries like flex-layout employ this for various responsive sizes: fxLayout.gt-sm fxAlign.sm Can the same concept be applied to a component&a ...

``The Vue.js routing system is determined by the incoming data received

Working with VueRouter to navigate to a component based on payload. { path: '/foobar', name: 'foobar', component: foobar, } { path: '/foobar', name: 'foobarSuccess', component: foobarSuccess, query: { ...

Encountering an issue with Angular 1.6 and webpack: controller registration problem

Currently developing a small application with Angular for the frontend, and my frontend module is structured as follows: https://i.stack.imgur.com/tjfPB.png In the app.js file, the main Angular module 'weatherApp' is defined: angular.module(&a ...

Error encountered during Angular unit testing: Unable to read the 'id' property of a null value. (Jasmine, Karma)

I am currently working on writing unit tests for a specific component in my Angular application. The component uses a currentUser variable both in the component logic and the HTML template. I have hardcoded this variable by mocking it in every test using c ...

Display or conceal specific fields depending on the selection from a dropdown menu

I'm currently experimenting with using JavaScript and/or jQuery to dynamically hide specific HTML elements based on the selection in a drop down menu. Here is what my page setup looks like: [Drop down menu] [text field 1] [text field 2] [text field ...

Avoid receiving a 404 error when using an invalid ID

When trying to set up my workoutId param, I encounter the following error: UnhandledPromiseRejectionWarning: CastError: Casting to ObjectId failed for value "5fb02bd8b61abc02" at path "_id" for model "Workout" If the workou ...

Encountering an error during the registration process of @fastify/middie

I am currently in the process of integrating Fastify into a small project I am working on. One of the key requirements for this project is the utilization of Middleware (via @fastify/middie). However, when I follow the necessary steps to register the middi ...

How do I prevent my image slider from scrolling to the top of the page when I click next or prev?

<script> export default { name: "ImageSlider", data() { return { images: [ "https://cdn.pixabay.com/photo/2015/12/12/15/24/amsterdam-1089646_1280.jpg", "https://cdn.pixabay.com/photo/2016/02/17/2 ...

What is the best way to reload DataTables using an ajax/error callback?

In my code, I am customizing the default settings of DataTables like this: $.extend(true, $.fn.dataTable.defaults, { lengthChange: false, deferRender: true, displayLength: 25, stateSave: false, serverSide: true, processing: true, ...