The 'undefined' type cannot be assigned to the 'never' type

interface A {
  name?: string
  age: number
}

var a: A = {
  name: '',
  age: 23
}

var result:A = (Object.keys(a) as Array<keyof A>).reduce((prev, key) => {
  if (a[key] || a[key] === 0) {
    prev[key] = a[key] // an error was reported regarding `Type 'undefined' is not assignable to type 'never'`
  }
  return prev
}, {} as A)

console.log(JSON.stringify(result))

This code snippet above is a reproduction of the issue.

It was observed that the code works with typescript@~3.4.0, however, it does not compile with typescript@^3.5.0. Reviewing the changelog between versions 3.4 and 3.5 did not provide any clues on this difference.

Hypothesizing that the problem might be due to the absence of an index signature, the following adjustment was made:

interface A {
  name?: string
  age: number
  [K:string]:any <-- added this line
}

var a: A = {
  name: '',
  age: 23
}

var result:A = (Object.keys(a) as Array<keyof A>).reduce((prev, key/* validation lost */) => {
  if (a[key] || a[key] === 0) {
    prev[key] = a[key]
  }
  return prev
}, {} as A)

console.log(JSON.stringify(result))

The previous error no longer occurs, but the type of key within the reduce callback function now becomes string|number, resulting in the loss of type validation for the key.

Is this the expected behavior?

If so, how can we resolve the issue of

Type 'undefined' is not assignable to type 'never'
while maintaining type checking for the key variable?

Answer №1

There was a significant change in TS 3.5 with the PR Improving the consistency of indexed access types:

Previously, an indexed access T[K] on the source side would result in a union type of selected properties by T[K], but now on the target side, it resolves to an intersection type of those properties. The previous behavior where the target side also resolved to a union type was considered unsound.

As seen in your example, prev[key] = a[key] now triggers an error because key has a union type of "name" | "age", and prev[key] on the target side resolves to an intersection type of all selected properties: A["name"] & A["age"], resulting in string & number or essentially never (with prev being of type A).

The rationale behind this inferred intersection for prev[key] is to ensure that all potential keys "name" | "age" of prev can be safely written to. Without knowing the actual value of key at compile time using keyof A, the changes introduced in the PR aim to enforce stronger typings.

The solution involves introducing generic type parameters for the object (prev) and/or property name (key). Examples provided by maintainers can be found here, here, and here. While not specific to your case, you could potentially refactor your code like this:

const result: A = (Object.keys(a) as Array<keyof A>).reduce(
  <K extends keyof A>(prev: A, key: K) => {
    // t[key] === 0 would only work for numbers
    if (a[key] /* || t[key] === 0 */) {
      prev[key] = a[key]
    }
    return prev
  }, {} as A)

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

Tips for refreshing a React component using incremental changes retrieved from an API

I am developing a unique React application using Next.js and TypeScript, with an api-backed data set in one component that needs to be cached indefinitely. Unlike traditional examples I have found online, my component must: Fetch only the most recent 100 ...

Unable to detect tsc after installing globally within Windows Sandbox

I followed the instructions provided here to install TypeScript globally. npm install -g typescript After installing both inside vscode and outside, I encountered an issue where tsc --version does not work and shows 'tsc is not recognized'. Int ...

Issues detected with the functionality of Angular HttpInterceptor in conjunction with forkJoin

I have a Service that retrieves a token using Observable and an HttpInterceptor to inject the token into every http request. It works seamlessly with a single request, but when using forkJoin, no response is received. Here is the code for the interceptor: ...

Having trouble creating a unit test for exporting to CSV in Angular

Attempting to create a unit test case for the export-to-csv library within an Angular project. Encountering an error where generateCsv is not being called. Despite seeing the code executed in the coverage report, the function is not triggered. Below is the ...

After selecting an item, the Next UI navbar menu seems to have trouble closing

Having trouble with the navbar menu component not closing when an option is selected from the menu. The menu does open and close successfully within the menu icon. I attempted to use onPress() but it doesn't seem to be working as expected. "use c ...

Obtain the name of a node using its identification number in D3JS

I am currently working on implementing a generalized tooltip feature. This tooltip will display the name and other relevant data of the active node. For example, if node 3 is currently active, the tooltip will show the name and distance (not link distance) ...

I need RxJs to return individual elements to the subscriber instead of an array when using http.get

I've been developing an Angular 2 app (RC5) with a NodeJS backend RESTful API integration. One specific route on the backend returns an array of 'Candidates': exports.list = function (req, res, next) { const sort = req.query.sort || null ...

Using Jest and Supertest for mocking in a Typescript environment

I've been working on a mock test case using Jest in TypeScript, attempting to mock API calls with supertest. However, I'm having trouble retrieving a mocked response when using Axios in the login function. Despite trying to mock the Axios call, I ...

Struggling with TypeScript compilation in a Vue.js project? Encounter error code TS2352

Here is my code snippet from window.ts import Vue from 'vue' interface BrowserWindow extends Window { app: Vue } const browserWindow = window as BrowserWindow export default browserWindow Encountering a compilation error Error message: TS2 ...

Having trouble with JavaScript's Date.getUTCMilliSeconds() function?

I have a straightforward question for you. Take a look at this Angular App and try to create a new date, then print the number of UTC milliseconds of that date in the console. Can you figure out why it is returning zero? ...

When using Angular 10 or later, the mouseclick event will consistently trigger Angular's change detection mechanism

When I bind an event to elements on a component's HTML page, the component continues to detect changes even when the element event is fired. Despite setting the change detection mode of the component to OnPush, this behavior persists. To test this, I ...

A more efficient method for querying documents based on ids that are not in a given list and then sorting them by a specific publish date

After implementing the code provided below, I noticed that the performance tests indicate each request takes a second or longer to complete. My goal is to enhance this speed by at least 10 times. The bottleneck seems to be caused by the NOT operator resu ...

Transform HTML elements within an *ngFor iteration based on a specific variable in Angular 4

In my current project using Angular 4, I am faced with the task of dynamically modifying HTML tags within an *ngFor loop based on a variable. Here is the code snippet that represents my approach: <mat-card-content *ngFor="let question of questionGrou ...

Transmitting data from Angular to .NET Core for seamless integration

I have been attempting to send an xls or any other file from my angular application to a .NET core controller, but none of my methods seem to work... Below is my component where I call my service upon button click: handleFileInput(file: FileList) { this. ...

Executing a series of imported functions from a TypeScript module

I'm developing a program that requires importing multiple functions from a separate file and executing them all to add their return values to an expected output or data transformation. It's part of a larger project where I need these functions to ...

Load information into a different entity

I need help with adding new values to an existing object. When I receive some form data from noteValue, I also have additional input data in my component under person that I would like to integrate into noteValue before saving it. let noteValue = form.va ...

tsc is not recognizing the configurations in my tsconfig.json file

Running tsc in my project's directory is causing an error to be outputted (as shown below). This is my first attempt at using TypeScript and Node.js. Please consider me as a complete beginner. Operating system: Ubuntu 15.10 64bits NPM version: 2.4. ...

Enabling specific special characters for validation in Angular applications

How can we create a regex pattern that allows letters, numbers, and certain special characters (- and .) while disallowing others? #Code private _createModelForm(): FormGroup { return this.formBuilder.group({ propertyId: this.data.propertyId, ...

What steps can be taken to initiate Nx Release on the apps/* when modifications are made to the connected libs/* modules?

Trying out the nx release command but struggling to get it to release an app when there are changes to a dependent module. Examining the graph below, you can see that I have 3 apps, with 2 depending on the shared-ui module. If I directly modify the apps, ...

Setting up a Form submit button in Angular/TypeScript that waits for a service call to finish before submission

I have been working on setting up a form submission process where a field within the form is connected to its own service in the application. My goal is to have the submit button trigger the service call for that specific field, wait for it to complete suc ...