The data type 'A | B' cannot be assigned to type 'A & B'

What could be causing the compilation error in this code?

type A = { n: number }
type B = { s: string }
type Thing = {
  a: A
  b: B
}
function update(obj: Thing, path: keyof Thing) {
  obj[path] = obj[path]
}

It seems like both sides of the assignment should have type A | B, but unfortunately the TypeScript compiler returns an error:

error TS2322: Type 'A | B' is not assignable to type 'A & B'.
  Type 'A' is not assignable to type 'A & B'.
    Property 's' is missing in type 'A' but required in type 'B'.

10     obj[path] = obj[path]
       ~~~~~~~~~

Any suggestions on how to resolve this and make it compile successfully?

Answer №1

Check out this new code snippet:

function modify<T extends Item>(item: T, property: keyof T) {
  item[property] = item[property]
}

Answer №2

When you access the obj[path] syntax, it may result in either an A or a B. The compiler lacks the information to determine which type it will be, and therefore will not allow the assignment unless all conditions are met:

type key = keyof Thing;
const keys: key[] = ['a', 'b'];

const path = keys[Math.round(Math.random())];
const thing: Thing = {
    a: {
        n: 3
    },

    b: {
        s: 'hi'
    }
};

const result = thing[path]; // A | B, undetermined by the compiler!

Therefore, when using path to access a Thing, the compiler cannot infer whether it is an A or a B. Although we understand that they are equivalent, the compiler is unable to confirm this due to lack of evidence. This same concept applies for the funarg.

If something is statically determinable, the compiler allows the assignment as shown below:

function update(obj: Thing) {
  obj.a = obj.a;
  obj['a'] = obj['a'];
}

Playground

This explanation covers the 'why'. If you are interested in how to help the compiler understand the correct type, refer to this comprehensive answer

Answer №3

This issue arises from modifications made in Typescript 3.5 with the aim of enhancing the integrity of indexed access types.

Previously, when an indexed access T[K] was used on the source side of a type relationship, it would result in a union type of the selected properties by T[K]. However, on the target side, it now resolves to an intersection type of the properties chosen by T[K]. This update aims to address issues with unsoundness that arose when the target side also resolved to a union type.

type A = { n: number }
type B = { s: string }
type Thing = { 
  a: A
  b: B
}

declare const thing: Thing

const key = 'a' as keyof Thing

let t = thing[key] // t has type A | B
t = { s: '' }      // we can `safely` assign it a value of type `B`
 
thing[key] = t     // however, this is where problems arise

Explore playground link


Check out this answer for a practical example.

Answer №4

To allow for flexibility in key types, you can create an index signature that accepts any key and assigns a value that is an intersection of A & B.

type A = { n: number }
type B = { s: string }
type AB = A & B;
type Entity = {
  [key: string]: AB;
}
function modify(entity: Entity, identifier: keyof Entity) {
  entity[identifier].n = 0;
  entity[identifier].s = "hello";
}

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

The Jest test is experiencing a failure as a result of an imported service from a .ts file

In my Jest test file with a .spec.js extension, I am importing an index.js file that I need to test. Here is the code snippet from the .spec.js file: var HttpService = require('./index.js').HttpService; The problem arises because the index.js f ...

Determining the data type of a generic variable within an Angular component

I'm currently in the process of developing a versatile component that can handle data of only two specific types: interface X{ name: string, path: string, type: string, } interface Y{ name: string, path: string, } Both types X a ...

Error: Unable to locate module: Could not find 'react-server-dom-webpack/client.edge'

I've been trying to incorporate server components into my nextJS project, but I keep encountering an issue when using "use server" in my component. Error message: `./node_modules/next/dist/build/webpack/loaders/next-flight-loader/action-client-wrappe ...

When working with TypeScript in Node, the module ""http"" does not have a default export available

const httpModule = require('http'); httpModule.createServer((req, res) => { res.end('Hello World'); }).listen(3000, () => console.log('Server is running on port 3000')); I've installed @types/node but ...

attempting to refine an array of objects using another array within it

I am currently filtering a group of objects in the following manner: [ { "Username":"00d9a7f4-0f0b-448b-91fc-fa5aef314d06", "Attributes":[ { "Name":"custom:organization", "Valu ...

Using styled-components and typescript to override props

Currently, I am faced with the challenge of using a component that necessitates a specific property to be set. My goal is to style this component with the required property already defined, without having to manually specify it again during rendering. Howe ...

Issue with Angular 6 where data is not binding to the UI on initialization

I am struggling with binding dynamic data to the UI and Data tables on my website. Despite trying various methods, I have not been able to achieve success. Currently, I am using the smart admin latest theme for development. When I make a call to the API, ...

Exploring Angular 8 Route Paths

Working on an Angular 8 project, I encountered an issue with my code: src/app/helpers/auth.guard.ts import { AuthenticationService } from '@app/services'; The AuthenticationService ts file is located at: src/app/services/authentication.servic ...

Clicking a button in React requires two clicks to update a boolean state by triggering the onClick event

I have a React functional component with input fields, a button, and a tooltip. The tooltip is initially disabled and should only be enabled and visible when the button is clicked and the input fields contain invalid values. The issue I'm facing is t ...

The preflight request in Angular2 is being rejected due to failing the access control check: The requested resource does not have the 'Access-Control-Allow-Origin' header

I encountered an issue while attempting to execute a basic POST request to establish an account using an API in .NET. The process fails with the mentioned warning title. Interestingly, performing the same request in Postman (an API testing tool) yields a s ...

Can you provide information on the type signature of the `onChange` event for the MUI DatePicker?

I am utilizing an instance of the MUI DatePicker along with a MomentAdapter: import *, {useState} as React from 'react'; import TextField from '@mui/material/TextField'; import { AdapterMoment } from '@mui/x-date-pickers/AdapterMom ...

Best approach to inform pages about a variable update in Ionic

Imagine a scenario where we have a page called ListItemPage displaying a list of items: Within the ts file, there is a variable defined as items: any = []; In the html file, we find <ion-item *ngFor="let item of items"> Users can click on a (+ ...

Tips for ensuring the HTML checkbox element is fully loaded before programmatically selecting it

When a user accesses the page, I want certain checkboxes to be automatically checked. To achieve this, I am storing the IDs of the HTML checkbox elements in a service. Upon entering the page, the service is utilized to retrieve an array containing these ID ...

Breaking up and Substituting text within Angular 8's HTML structure

When I retrieve data from a REST api, I need to split the name parameter at '2330' and insert a line break. For example, if the name is: ABCD 2330 This is My Name, I want the output on my screen to appear as: ABCD 2330 This is My Name // this par ...

The 'target' property is not found on the type 'KeyboardEventHandler<HTMLInputElement>'

My Visual Studio Code is giving me an error in my onKeyUp function when I try to access the input target and retrieve its value. import React from 'react'; import styles from './styles.module.scss'; export function Step3() { ...

Can someone guide me on creating a slideshow using Ionic?

Having trouble integrating a javascript/css slideshow into Ionic. Ionic does not support the use of ".style" in elements. Need assistance, below is the code: <head> <title>Slideshow</title> <style> .slides {display:none;} </styl ...

Ways to transfer an Object from a service to a component

I'm currently working on my website and trying to implement a cart feature where users can add items. To achieve this, I have created a service that contains the cart as an object called cart. The service has functions to add items to the cart and ret ...

AngularJS Constants in TypeScript using CommonJS modules

Let's talk about a scenario where I need to select a filter object to build. The filters are stored in an array: app.constant("filters", () => <IFilterList>[ (value, label) => <IFilterObject>{ value: value, label: label } ]); i ...

Specifying the return type of a function as a combination of the types of the input arguments

Is there a way to safely implement the given function in TypeScript without using unsafe casts or an extensive number of function overloads with various input permutations? interface Wrapped<T> { type: string; data: T; } interface WrappedA&l ...

Is the regex returning the correct result?

I need to discuss the date field with a format of YYYYMMDD, as shown below: zod.string().max(8).regex(new RegExp('^(19[0-9][0-9]|20[0-9][0-9]|[0-1][0-9]{3})(1[0-2]|0[1-9])(3[01]|[0-2][1-9]|[12]0)$')); The value provided is 20001915. The definit ...