Include type declarations for property values that resemble arrays in a generic object

Imagine you have a function that:

  1. receives an object with multiple properties and a property name;
  2. checks if the property holds an array;
  3. if it does, performs an action (such as printing the values it contains)

Here's an illustration:

function processIfPropertyIsArray(values, field) {
  if (Array.isArray(values[field])) {
    values[field].map(x => console.log(x));
  }

  return null;
}

The following types appear to be functioning correctly, with ArrayValues<T> exclusively retrieving array-like types from T's properties:

type StringKey<T> = keyof T

type ArrayValues<T> = T[
  {
    [K in keyof T]: T[K] extends any[] ? K : never
  }[keyof T]
]

type X = {
  foo: string,
  bar: string[],
  qux: number[],
}

// const arrayValues: string[] | number[] -> Success!
const arrayValues: ArrayValues<X> = ['a'];

However, when attempting to apply these types in the function above, Typescript throws an error:

function processIfPropertyIsArray<T>(values: T, field: keyof T) {
  if (Array.isArray(values[field])) {
    const arrayValue = values[field] as ArrayValues<T>;
    arrayValue.map(x => console.log(x));
  }

  return null;
}

https://i.sstatic.net/CJQ6L.png

Property 'map' does not exist on type 'T[{ [K in keyof T]: T[K] extends any[] ? K : never; }[keyof T]]'

Why is this happening? What could be the issue/mistake here?

Answer №1

The problem arises from the fact that Array.isArray does not narrow the type of values[field] because the latter is not an expression that can be narrowed down. By assigning values[field] to a variable first, the type-checking is done smoothly without any complex manipulations:

function displayIfKeyIsArray<T>(values: T, field: keyof T) {
  const p = values[field];
  // inferred type p: T[keyof T]
  if (Array.isArray(p)) {
    // narrowed type to p: T[keyof T] & any[]
    p.map(x => console.log(x));
  }

  return null;
}

Playground Link

If you are looking to correct the ArrayValues generic type, just shift the T[...] to target K instead of the entire entity:

type ArrayValues<T> =
  {
    [K in keyof T]: T[K] extends any[] ? T[K] : never
  }[keyof T]

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

Angular: Display an element above and in relation to a button

Before I dive in, let me just mention that I have searched extensively for a solution to my problem without much luck. The issue is describing exactly what I'm trying to accomplish in a way that yields relevant search results. If you're interest ...

Is it possible for us to perform an addition operation on two or more items that belong to the same

I am faced with a challenge involving 3 objects of the same type, each having different values for their properties. My goal is to add them together as illustrated below: Consider this scenario: objA = { data: { SH: { propertyA: 0, propertyB: ...

Oops! Angular2 couldn't find a provider for HttpHandler

I have been working on implementing HttpCache through an interceptor. Below is the code snippet for caching-interceptor.service.ts: import { HttpRequest, HttpResponse, HttpInterceptor, HttpHandler, HttpEvent } from '@angular/common/http' import ...

Show the Array List in a left-to-right format

Is there a way to display an array list from left to right with a div scroll, instead of top to bottom? I am having trouble achieving this. Here is my demo code for reference. HTML <div> <h2 class="ylet-primary-500 alignleft">Sessions</h ...

Challenge with the scope of 'this' in Typescript

Whenever I invoke the findFromList function from a component, it triggers this particular error message: ERROR TypeError: Cannot read property 'searchInArray' of undefined at push../src/app/shared/services/filter.service.ts.FilterService ...

Trouble with displaying points in Angular2's highcharts

I have implemented the angular2-highcharts chart module in my angular2 web application. Everything works fine when the graph has less than 7000 points, with the line and points displaying correctly. However, once the number of points surpasses 7000, there ...

My Nextjs project is encountering deployment issues with both Netlify and Heroku

Whenever I attempt to deploy my application on Heroku or Netlify, I encounter an ERROR related to an incorrect import path. It's perplexing because the import is accurate and functions properly locally. Log ./pages/_app.tsx:7:27 6:31:19 PM: Type err ...

Updating the image source attribute using Vue.js with TypeScript

Let's discuss an issue with the img tag: <img @error="replaceByDefaultImage" :src="urls.photos_base_url_small.jpg'"/> The replaceByDefaultImage function is defined as follows: replaceByDefaultImage(e: HTMLImageElement) ...

Automatically convert TypeScript packages from another workspace in Turborepo with transpilation

I have set up a Turborepo-based monorepo with my primary TypeScript application named @myscope/tsapp. This application utilizes another TypeScript package within the same repository called @myscope/tspackage. For reference, you can view the example reposit ...

Enhancing JavaScript functions with type definitions

I have successfully implemented this TypeScript code: import ytdl from 'react-native-ytdl'; type DirectLink = { url: string; headers: any[]; }; type VideoFormat = { itag: number; url: string; width: number; height: number; }; type ...

What is the best way to bring in the angular/http module?

Currently, I am creating an application in Visual Studio with the help of gulp and node. Node organizes all dependencies into a folder named node_modules. During the build process, gulp transfers these dependencies to a directory called libs within wwwroo ...

The sequence of initializing test hooks in inconsistent playwright tests

My testing framework setup looks something like this: test.describe("...", () => { let p: Page; test.beforeEach(async({browser}) => { p = await (await browser.newContext()).newPage(); } test(...); test(...); test.aft ...

What is the syntax for typing a mongoose populated field in Typescript?

I am faced with a field that can either be an ID or have a value, and I am attempting to type this field using TypeScript import { InferSchemaType, Schema, Types, model } from 'mongoose'; const personSchema = new Schema({ _id: Schema.Types.Obj ...

Utilizing a variable to pass props to a component (instead of a static component) within React Router 5

In react-router 5, you can pass props to a child component in this way: <Route path="/" exact render={ props => <MyPage title={myTitle} dataPath={myDataPath} {...props} />} /> However, I am using a route model in my ...

Utilize Angular 5 to implement URL routing by clicking a button, while also preserving the querystring parameters within the URL

I have a link that looks like this http://localhost:4200/cr/hearings?UserID=61644&AppID=15&AppGroupID=118&SelectedCaseID=13585783&SelectedRoleID=0 The router module is set up to display content based on the above URL structure { path: & ...

Angular 2: Sending an HTTP GET request with custom headers and parameters

I have been encountering difficulties while attempting to retrieve data from a Stardog triple store into Angular. Despite successfully accessing the service using curl with matching headers and parameters, I am unable to replicate this functionality with ...

Changes in the styles of one component can impact the appearance of other

When it comes to styling my login page, I have specific stylesheets that I include in login.component.ts. For all the common CSS files, I have added them in the root index ("index.html") using the traditional method. However, after a user logs into the sys ...

What is the reason behind Rollup flagging code-splitting issues even though I am not implementing code-splitting?

In my rollup.config.js file, I have only one output entry defined as follows: export default { input: './src/Index.tsx', output: { dir: './myBundle/bundle', format: 'iife', sourcemap: true, }, plugins: [ ...

Tips for converting API data to DTO (Data Transfer Object) using TypeScript

Here is an array of vehicles with their details. export const fetchDataFromApi = () => { return [ { vehicleId: 1, vehicleType: 'car', seats: 4, wheelType: 'summer', updatedAt: new Date().toISOString }, { vehicleId: 2, vehic ...

Learn how to break down Angular 2 with Typescript in just 5 minutes by troubleshooting issues

I've been delving into the world of TypeScript and Angular 2 by following the guide at https://angular.io/guide/quickstart. After going through all the steps, I encountered some errors with the final step npm start. Here's what I got: Microsoft ...