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

Angular 6 TypeScript allows for efficient comparison and updating of keys within arrays of objects. By leveraging this feature

arrayOne: [ { id: 1, compId: 11, active: false, }, { id: 2, compId: 22, active: false, }, { id: 3, compId: 33, active: false, }, ] arrayTwo: [ { id: 1, compId: 11, active: true, }, { id: 2, compId: 33, active: false, ...

Establishing a typescript class as an interface

While attempting to upgrade to TypeScript 3.5, I ran into an issue with a signature that has been overlooked by the ts-compiler, despite being present for years. A third-party framework (knockoutJS) requires me to pass something that adheres to this: int ...

Using Promise<void> instead of Promise<any> is the preferred approach

Working with AngularJS, I have created several asynchronous functions that all use the same signature, which is app.Domain.GenericModel.EntityBase (my generic model). Here is an example: get(resource: string): ng.IPromise<app.Domain.GenericModel.Entity ...

Unit testing Firebase function with Jest for mocking

Currently, I am in the process of developing unit tests and facing challenges with mocking Firebase functions while specifying the return type upon calling them. The code snippet below illustrates what I intend to mock (account.service.ts) and provides ins ...

Using TypeScript with React and Material-UI: Issue with undefined theme in createStyles()

Currently, I am delving into React with TypeScript and utilizing the Material UI framework for the frontend. In my quest to activate media queries, an error has crossed my path: Uncaught TypeError: Cannot read property 'up' of undefined ...

Having trouble capturing emitted events from a child component in Angular 2+?

I have a question as a beginner. I'm trying to send a message from the child component to the parent component but for some reason, it's not working. app.component.html <app-cart-component> [items]="rootItems" (outputItems)=&quo ...

There is no matching overload for this call in React Native

I am working on organizing the styles for elements in order to enhance readability. Here is the code I have written: let styles={ search:{ container:{ position:"absolute", top:0, }, } } After defining the s ...

Transforming AngularJS 2.0 code into ES6 syntax

Successfully implemented the AngularJS 2.0 5 Minute Quickstart in my IntelliJ IDEA 14.1.4 following a helpful Stackoverflow Answer on AngularJS 2.0 TypeScript Intellij idea (or webstorm) - ES6 import syntax. However, it seems to focus on compiling TypeScr ...

Experimenting with nested dual dynamic routing within the app's directory

Currently working with NextJS 13 and executing the following operations within the app directory. I am attempting to utilize the generateStaticParams function to generate static pages during build time. The route structure is: subpage/[categoryName]/[gif ...

Discovering the data types for node.js imports

When starting a node.js project with express, the code typically begins like this - import express = require('express') const app = express() If I want to pass the variable 'app' as a parameter in typescript, what would be the appropri ...

Executing Promises in a loop: TypeScript & Angular with IndexedDB

Currently, I am working on a data synchronization service where data is being retrieved from a web service and then stored in IndexedDB. In my TypeScript Angular Service, the code looks something like this: this.http .post(postUrl, postData) .suc ...

The data from Angular2 Observable Subscription appears undefined, although a closer look at the Browser Debug reveals otherwise

Is it possible there is an issue with the data subscription process? Upon subscribing to data from a service call, 'undefined' is returned as the data set. Surprisingly, when I debug the code in the browser, it clearly shows that the correct dat ...

What is the best way to set up a reactive form in Angular using the ngOnInit lifecycle

I have been facing an issue while trying to set up my reactive form with an observable that I subscribed to. Within the form class template, I used the ngOnInit lifecycle hook to fetch the desired object, which is the product. The first code snippet repre ...

Using TypeScript along with Nuxt.js and Vuex to access methods from an imported class

Currently, I am in the process of developing a nuxt.js application with typescript and my goal is to segregate the API Calls from the vuex store. However, I've encountered an issue where it seems like I cannot utilize the methods when importing the cl ...

Discovering the worth of a variable outside of a subscription or Promise within Ionic 3

Apologies for my English. I am encountering an issue when attempting to view the results of a REST API using both subscribe and Promise methods. Within my provider, I have the following code: Provider: import { HttpClient } from '@angular/common/h ...

Limitations require a member to only accept a type (and not an instance) that extends or implements another type [TypeScript]

I'm seeking assistance with a set of abstract concepts in TypeScript. I am looking to restrict a member to only accept types as values, but those types must also implement or extend other types or interfaces. For example: The code snippet below is ...

Error Alert: Redundant Identifier in Angular 2 TypeScript Documents

After following the Angular2 TS Quickstart guide, I noticed duplicate files scattered across various folders in my project. For browser: typings/browser node_modules/angular2/typings/browser Regarding es6-shim: node_modules/angular2/typings/es6-shi ...

When utilizing makeStyles in @mui, an error may occur stating that property '' does not exist on type 'string'

I am stuck with the code below: const useStyles = makeStyles(() => ({ dialog: { root: { position: 'absolute' }, backdrop: { position: 'absolute' }, paperScrollPaper: { overflow: 'visib ...

Exploring the Ways to Determine Array Type in Typescript Generics

I'm working with a method that looks like this: public select(fieldName: keyof TType) In this scenario, TType can potentially be an array type. If fieldName is called with a type of User[], I want to access the properties of User instead of the defa ...

Error in Protractor Typescript: The 'By' type does not share any properties with the 'Locator' type

https://i.stack.imgur.com/8j2PR.png All the different versions Error. Protractor version : 5.2.0 npm : 3.10.10 node :6.9.5 typescript :2.6.0 The 'By' type does not share any properties with the 'Locator' type What is the solution to ...