Typescript error: the argument passed as type a cannot be assigned to the parameter of type b

In my programming interface, I have defined shapes as follows:

type Shape = | Triangle | Rectangle;
interface Triangle {...}
interface Rectangle {...}

function operateFunc(func: (shape: Shape) => void) {...}

function testFunction() {
    const rectFunc = (rect: Rectangle) => {...};
    operateFunc(rectFunc); // Throws an error saying the argument of type a is not assignable to parameter of type b...
}

However, when I use the following code snippet, everything works just fine:

function calculateArea(shape: Shape) {
  .....
}


const triangleShape: Triangle = {...};
const rectangleShape: Rectangle = {...}

calculateArea(triangleShape);

I find it intriguing that the Rectangle type is compatible with Shape in the calculateArea function but not in the testFunction. What would be the best practice to handle this issue in the testFunction?

You can view the reproducible code snippet here:

Answer №1

When looking at your provided example, we see that Rectangle is considered part of the Shape union.

The interesting scenario arises with the computeArea function, as it can work with any shape, including a Rectangle.

However, it becomes counterintuitive when dealing with the foobar function. This function expects a callback that handles a Shape, but using a callback designed for a Rectangle results in an error...

This situation highlights the concept of callback arguments being contravariant. In this case, if foobar needed a callback to handle a Rectangle, calling it with computeArea would have been appropriate.

To better grasp why the example does not function as expected, picture foobar constructing an arbitrary Shape internally (potentially a

Triangle</code), and trying to process it with the given callback - which may not be suitable due to its inability to handle a <code>Triangle
.

If foobar explicitly required a callback for a Rectangle, then it would only execute that callback with a Rectangle argument. Therefore, a callback capable of handling any Shape (including a Rectangle) like computeArea would have worked perfectly fine.


In the context of your code snippet, it appears that the foobar function is essentially:

function getShapeArea(shape: Shape, computeArea: (shape: Shape) => number) {
  const area = computeArea(shape);
  return area;
}

During attempts to call this function using:

getShapeArea(circle, computeCircleArea);

...where circle represents a Circle (also within the Shape union), and computeCircleArea is a function expecting a Circle and returning a number, TypeScript issues an error.

Despite the TypeScript error message, the transpiled JavaScript code runs without errors during execution.

If the callback argument were covariant, attempting to use the function with:

getShapeArea(triangle, computeCircleArea);

...would most likely result in an Exception (since triangles do not have a radius).

A universal computeArea callback capable of working with any Shape illustrates contravariance of the callback argument. It enables processing of any actual shape passed as the first argument of the getShapeArea function with that hypothetical computeArea callback.

To address your specific scenario, providing TypeScript with additional hints regarding the relationship between the two arguments of getShapeArea may prove beneficial. Consider incorporating generics:

function getShapeArea<S extends Shape>(shape: S, computeArea: (shape: S) => number) {
  const area = computeArea(shape);
  return area;
}

By implementing this approach, utilizing

getShapeArea(circle, computeCircleArea)
no longer triggers a TS error!

Furthermore,

getShapeArea(triangle, computeCircleArea)
clearly indicates an incorrect usage (as computeCircleArea cannot handle the triangle).


Depending on the specifics of your situation, enhancing the getShapeArea function to automatically detect the shape and utilize the corresponding compute function (without requiring explicit specification each time as a second argument) using type narrowing could offer improvements:

function getShapeArea(shape: Shape) {
  // Utilizing the "in" operator narrowing
  if ("radius" in shape) {
    return computeCircleArea(shape); 
  } else if ("width" in shape && "length" in shape) {
    return computeRectangleArea(shape);
  }
}

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

Utilize INTERFACE-driven capabilities in ReactJS to dynamically render components based on a variable

How can interface-based functionality be used to render components based on tab selection? If we have 3 components as follows: OneTimeOnlyScheduleComponent DailyScheduleComponent WeeklyScheduleComponent We aim to have all of these components implement t ...

The options object provided for Ignore Plugin initialization in Webpack 5.21.2 does not conform to the API schema, resulting in an error

Here is the setup of my webpack.config.js on a backend server running webpack version 5.21.1: /* eslint-disable */ const path = require('path'); const webpack = require('webpack'); module.exports = { target: 'node', modul ...

What methods can be used to create data for the child component?

Below is the primary code: import {Col,Container,Row} from 'react-bootstrap'; import {useEffect,useState} from "react"; import AppConfig from '../../utils/AppConfig'; import BB from './BB'; import React from "re ...

Encountering an unrecoverable SyntaxError while trying to deploy a website on Netlify

When using commands like npm start, npm run build, and pm2 start server.js, everything runs smoothly without any errors. However, I encounter an issue when trying to deploy my project on Netlify. The Chrome console displays the error: Uncaught SyntaxError: ...

How come my total isn't zero after I get a 1 on the dice roll?

I've been working on a dice game where if either of the dice rolls a 1, the score is set to 1. However, I'm having trouble getting that part of my code to work properly. I'm confident that everything else is functioning correctly. v ...

Merging the outcomes of a JSON call

Presently, I have an async function that returns a JSON response in the form of an array containing two objects. Please refer to the screenshot. https://i.sstatic.net/gCP8p.png How can I merge these objects to obtain: [{resultCount: 100, results: Array(1 ...

The 'Group' type is lacking the 'children' properties needed for the 'Element' type in Kendo UI Charts Drawing when using e.createVisual()

In my Angular 10 project, I utilized the following function to draw Kendo Charts on Donut Chart public visual(e: SeriesVisualArgs): Group { // Obtain parameters for the segments this.center = e.center; this.radius = e.innerRadius; // Crea ...

Error encountered: `npm ERR! code E503`

While attempting to execute npm install on my project, which was cloned from my GitHub repository, I encountered the following error: npm ERR! code E503 npm ERR! 503 Maximum threads for service reached: fs-extra@https://registry.npmjs.org/fs-extra/-/fs-ex ...

Async pipe in Angular does not work with my custom observables

I've been trying to implement the async pipe in my template, but I'm encountering difficulties retrieving data from my API. To store and retrieve the data, I have utilized a behavior subject to create an observable. However, when I attempt to dis ...

Retrieve information using PHP with AJAX without revealing it on the screen

Is it feasible to fetch data using Ajax in PHP and store them in a JS variable without displaying it? I require this data for date manipulation but do not want to show it. When I attempted to merely return the data without echoing it, the Ajax data in JS ...

<T extends object>(value: T): T, but with the type changing from null to string

I discovered a tool called pathmirror that transforms objects like: {a: {b: null} } to {a: {b: 'a.b'} This is particularly useful for naming Redux actions. I'm wondering how I can create a type definition for this? Currently, my declarat ...

Filtering an array dynamically in Typescript depending on the entered value

My task involves filtering arrays of objects based on input field values. Data data: [{ taskname: 'Test1', taskId: '1', status: 'Submitted' }, { taskname: 'Test2', taskId: '2', status: 'Re ...

How can Express JS be configured to make URL calls from a local environment?

I encountered an issue with my code (Weather App using OpenWeatherMap Api) when I attempted to move my apiKey and apiUrl to the .env file. An error appeared in the terminal, but it's unclear why it occurred. Below is my code: const express = require( ...

What was the process for implementing the lexer and parser?

My journey into the depths of programming languages led me to explore how TypeScript is implemented, prompting me to venture into its Github repository. Within the language's source code under /src/compiler, I stumbled upon intriguing files like scan ...

Initiate a timer with intervals of 3 seconds upon reaching a designated section in a React application

useEffect(() => { console.log(window.scrollTo) console.log(textInput.current.offsetTop); }, [textInput,]) click here for more information check out the bottom of this page for a similar countdown feature - any ideas on how to implement it? ...

Is it possible to jest at a module function that is both exported and utilized within the same module?

Just diving into unit testing and learning about spies, stubs, and mocks. I've been trying to test the verify method in password.js as shown in the code snippet below. However, I'm having trouble creating a stub for the hash function within the ...

Issues with implementing AddEventListener in InAppBrowser on IONIC 2

I am currently working on implementing AddeventListener to listen for 'Exit' and 'LoadStart' events in InAppBrowser within IONIC2. Here is my HTML: <button (click)="browsersystem('https://www.google.com')" > Visit URL& ...

Improving the functionality of the JavaScript key press function

I have a small javascript snippet on my website that allows me to navigate the site using the arrow keys on my keyboard. document.onkeydown = function(evt) { evt = evt || window.event; switch (evt.keyCode) { case 37: ...

standalone visuals generated interactively with matplotlib

While I appreciate the plots generated by matplotlib and the ability to save them as SVG, there is a feature missing that I would like to see added... I am looking for a way to save the figure as an SVG file with embedded JavaScript code to add interactiv ...

HTML5 allows users to choose data from a list using a ComboBox and submit the 3-digit code

I'm looking to enhance my form by including an input box where users can select airports, similar to the functionality on this website (). When typing in the Destination Input, I want users to see a list of possible values with detailed suggestions su ...