Restrict the object field type to an array by analyzing control-flow for accessing elements within brackets

Enhancements in type narrowing using control-flow analysis for bracket element access have been introduced in TypeScript 4.7.

One thing I'd like to do is verify if the accessed field is an array or not. Currently, the type guard seems to be ineffective:

Error: Type 'never[]' is not compatible with type 'string'.(2322)

https://i.stack.imgur.com/vZxID.png

Despite utilizing isArray, TypeScript still considers the accessed field as potentially a string type instead of an array.

What could be missing here?


Below is a detailed example:

Let's say we define a type called Post, where certain fields are arrays:

type Post = {
  id: string;
  title: string;
  chapters: PostChapter[];
};

Now, suppose we have a list of strings (keys of a Post) that we want to use to update the value in a Post object dynamically:

const fieldNamesToReplace: (keyof Post)[] = ["chapters"];

When attempting to access these keys using brackets, TypeScript fails to recognize them as arrays, even after validation through Array.isArray.

By the way, one workaround that functions is creating a new object and replacing the field, avoiding reliance on control-analysis for bracketed access.


Here is a link to the playground along with the complete example:

Playground link

type PostChapter = {
  id: string;
  chapterTitle: string;
};

type Post = {
  id: string;
  title: string;
  chapters: PostChapter[];
};

const fieldNamesToReplace: (keyof Post)[] = ["chapters"];

const posts: Post[] = [
  {
    id: "1",
    title: "abc",
    chapters: [{ id: "1.1", chapterTitle: "def" }],
  },
];

const postsTransformed = posts.map((post) => {
  let postNew = { ...post };

  // works, because we don't rely on the type-narrowing for setting the value
  fieldNamesToReplace.forEach((fieldName) => {
    if (Array.isArray(postNew[fieldName])) {
      postNew = { ...postNew, [fieldName]: [] };
    }
  });

  // doesn't work
  fieldNamesToReplace.forEach((fieldName) => {
    if (Array.isArray(postNew[fieldName])) {
      postNew[fieldName] = [];
      // Error: Type 'never[]' is not assignable to type 'string'.(2322)

      const placeholder = postNew[fieldName];
      //      ^?
    }
  });

  return postNew;
});


Answer №1

The enhancements focus on string literals and symbols. This signifies that it will not function when the key is a variable, which seems to be causing your issue.

To address this issue, you can utilize a type predicate, but keep in mind that you won't be able to assign values to that variable afterward.

Check out this playground to see a demonstration of this concept.

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

Is there a way to retrieve keys of the object from this combination type?

Can someone please help me understand how to retrieve keys from this union type? The Value is currently being assigned as a never type. I would like the Value to be either sno, key, or id type Key = { sno: number } | { key: number } | { id: number }; typ ...

What is the solution to the error message "Uncaught TypeError: createTheme_default is not a function"?

While working on my react application with vite, typescript, and mui, I encountered the following error: enter image description here This issue seems to be connected to material ui. Sometimes, deleting the 'deps' folder in '\node_mod ...

Is there a way to operate both websocket and http methods concurrently on a server in Typescript?

I have a question regarding running a websocket server with the route "ws://localhost:port" using the 'ws' library. It requires the app instance of 'express' and also features http routes such as "http://localhost:port/auth/login". I am ...

It appears that the home page of next.js is not appearing properly in the Storybook

Currently, I am in the process of setting up my next home page in storybooks for the first time. Following a tutorial, I successfully created my next-app and initialized storybooks. Now, I am stuck at importing my homepage into storybooks. To achieve this, ...

Vue-Apollo - The 'value' property is not present in the 'Readonly<Ref<Readonly<any>>>' type

MY CURRENT DILEMMA: In my quest to seamlessly integrate vue-apollo v4 with Typescript, I have encountered a challenge. I am in the process of retrieving data from a simple query using useQuery along with useResult. The default return type of useResult i ...

Pass the type of object property as an argument in the function

I've been having trouble trying to figure this out and haven't been able to find a solution in the TS docs or examples I came across. Essentially, I'm working with a configuration specifying operations on object properties and looking to en ...

Discover the outcome of clicking on an object (mock tests)

I am just starting out with React and I'm unsure about when to use mocking. For instance, within the 'ListItem' component, there is a 'click me' button that reveals a dropdown for 'cameras'. Should I focus on testing what ...

Is it necessary to use Generics in order for a TypeScript `extends` conditional type statement to function properly?

Looking to improve my understanding of the extends keyword in TypeScript and its various uses. I recently discovered two built-in utilities, Extract and Exclude, which utilize both extends and Conditional Typing. /** * Exclude from T those types that are ...

The values of object keys are printed in a random order

Hey everyone, I have an object that looks like this var dates = { '2021-09-15': 11, '2021-09-16': 22, '2021-09-17': 38, '2021-09-18': 50, '2021-09-19': 65 }; I am trying to display the valu ...

I continue to encounter the same error while attempting to deliver data to this form

Encountering an error that says: TypeError: Cannot read properties of null (reading 'persist') useEffect(() => { if (edit) { console.log(item) setValues(item!); } document.body.style.overflow = showModal ? "hidden ...

Streamline copyright verification with Angular

We are currently working on an angular application that we plan to release as open-source. We make sure to include copyright information in every file, specifically in the .ts and .scss files. However, being human, there are times when we may forget to ad ...

Struggling to understand how to properly 'map' my data from the response in Next.js 13 using Typescript

Just a few days into my journey with next.js, and I'm already facing a challenge in figuring out how to fetch data from an API. In regular React, I would typically use useState and useEffect for managing state and fetching data. But when it comes to n ...

Transform array of elements from type T1 to element in the array to type T2

Here is a Typescript class I am working with: export class Envelope<T> { result: T; constructor(result: T) { this.result = result; } } I'm trying to convert Envelope<RecentPostResponse[]> to Observable<PostModel[]>: getP ...

What is the best way to generate conditional test scenarios with Protractor for testing?

Currently, there are certain test cases that I need to run only under specific conditions. it ('user can successfully log in', function() { if(siteAllowsLogin) { ..... } The problem with the above approach is that even when sitesNo ...

The TypeScript compiler throws an error when encountering nulls in conjunction with the isNull function

Whenever I set strictNullChecks: true in tsconfig.json and utilize the isNull function for null checks, the compiler throws the error TS2531: Object is possibly 'null'. Interestingly, isNull doesn't trigger any errors in VsCode, however, the ...

Having trouble launching the application in NX monorepo due to a reading error of undefined value (specifically trying to read 'projects')

In my NX monorepo, I had a project called grocery-shop that used nestjs as the backend API. Wanting to add a frontend, I introduced React to the project. However, after creating a new project within the monorepo using nx g @nrwl/react:app grocery-shop-weba ...

When a parameter is passed into a React-Query function with an undefined value, it can lead to the API returning a 404 error

Two parameters are sent from the frontend to trigger a GET request in another TypeScript file. It seems that one of the parameters is not successfully passed due to unknown rerenders, resulting in a 404 Error being returned by the API call in the console. ...

Is there a way to prevent the Drop event in Angular2?

In my Angular2 application, I have created a directive for an input field. To prevent paste or Ctrl+V within the host element of this directive, I used the following code which is functioning flawlessly: @HostListener('paste', ['$event&apos ...

Are you interested in creating dynamic tables/models with Sequelize?

Currently, I am exploring a theoretical question before diving into the implementation phase. The scenario is as follows: In my application, users have the ability to upload structured data such as Excel, CSV files, and more. Based on specific requirement ...

What's the best way to implement satisfies with a generic type?

In my development process, I am working with components that have default values combined with props. To streamline this process, I created a single function for all components: export function getAssignProps <T extends {}>(propsMass:T[]){ return ...