Best practice in Typescript for "Incompatible types for parameters 'X' and 'X'"

Can someone help me with a Typescript question I have?

demo-link

// Looking for the best way to handle Params and Options in this scenario

interface Params {
  name: string;
  age: number;
}

interface Option {
  callback: (params: Params | Params[]) => void
}

// My code snippet
const optionError: Option = {
  // Need params type as Params only
  callback: (params: Params) => { 
    console.log(params.name)
    console.log(params.age)
  }
}

const optionOK: Option = {
  callback: (params) => {
    const p = params as Params; // Is there an alternative to redefining the same parameter?
    console.log(p.name)
    console.log(p.age)
  }
}

Is there a different approach than defining the same parameter again?

Answer №1

Although it may not be exactly what you were searching for, this solution should address your problem:

interface Parameters {
  title: string;
  quantity: number;
}

interface Choice {
  action: (params: Parameters | Parameters[]) => void
}

const mistakeOption:Choice = {
  action:(params: Parameters | Parameters[]) => {
    if (!Array.isArray(params)) {
      console.log(params.title)
    }
  }
}

const correctOption:Choice = {
  action:(params: Parameters | Parameters[]) => {
    if (!Array.isArray(params)) {
      console.log(params.title)
    }
  }
}

We're defining the callback parameters correctly and then verifying if the parameters are of type Parameters or an array of Parameters by utilizing Array.isArray(). TypeScript will detect that if it's not an array, it is Parameters.

Additionally, it is recommended to refrain from using params as Parameters whenever possible to avoid overriding TypeScript typings, which could result in runtime errors if you can't guarantee that the provided argument will indeed be of type Parameters.

Answer №2

The reason for this occurrence is due to contravariance.

There exists a risky workaround that can help eliminate TypeScript errors. All you have to do is convert your callback property into a method by removing the arrow function.

As per TS documentation, TS methods are bivariant.


interface Params {
  name: string;
  age: number;
}

interface Option {
  callback(params: Params | Params[]): void // modification is evident here
}

const optionError: Option = {
  callback: (params: Params) => {
    console.log(params.name)
  }
}

const optionOK: Option = {
  callback: (params: Params[]) => {
    params.map(e => e) // valid
  }
}

Nevertheless, this approach is extremely unsafe. For a safer alternative, follow the solutions provided by @jeremynac.

Answer №3

Unsure if this can be considered the most optimal method, but if the goal is to indicate that in order to adhere to the Option type, you must create a callback function that accepts either Params or Params[] (or potentially both), it needs to be explicitly defined as follows:

interface Params {
  name: string;
  age: number;
}

interface OptionWithParams {
  callback:  (params: Params) => void
}

interface OptionWithParamsArray {
  callback:  (params: Params[]) => void
}

type Option = OptionWithParamsArray|OptionWithParams;

In that case, the following examples are all valid:


const optionOk1:Option = {
  callback:(params: Params) => {
    console.log(params.name)
  }
}


const optionOk2:Option = {
  callback:(params: Params[]) => {
    console.log(params[0].name)
  }
}
const optionOk3:Option = {
  callback:(params: Params|Params[]) => {
    console.log('It\'s all good');
  }
}

The last scenario should also work if implicit any is allowed, though ideally that shouldn't be necessary.

Playground link

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

Issue with Google Node SDK Calendar freebusy query failing to display event times for private calendars

I have been attempting to retrieve the free/busy times for a user whom I previously oauthed and stored their access token and refresh token in my database. However, every time I make the request, I consistently receive the following response: data: { ...

What causes me to create components with incorrect paths?

Can someone assist me with creating a new component in the dynamic-print folder instead of it being created in the app? Thank you ...

What advantages do interfaces as data types offer in Angular compared to using classes?

After watching a tutorial from my teacher, he showed us this code snippet: https://i.sstatic.net/MA3Z9.png He mentioned that the products array, defined as type any [], is not taking advantage of TypeScript's strongly typing. He suggested using an I ...

Is there a mistake in the TypeScript guide for custom typography in MUI5?

Currently, I am in the process of setting up custom typography variants in MUI5 by referencing this helpful guide: https://mui.com/customization/typography/#adding-amp-disabling-variants. As I follow step 2 and input the type definitions: declare module &a ...

Issues arise due to data inconsistency stemming from the combination of a for loop and .map() function within the BACK4APP

I seem to be facing a challenge with data consistency caused by the for (const object of results) {} loop in the Sandbox Link at line41. The issue is that the results are displayed as a single result after using the .map() method. However, when I perform a ...

Using Typescript to establish a connection between ngModel and an object's property

Let's talk about how we can dynamically bind an input to an undefined property in an object. For example, we have an object named user: let user = {}; How can we bind an input to a property that doesn't exist yet? Like this: <input [(ngMode ...

Developing an npm library with ReactJs: A step-by-step guide

As a newcomer to React, I am eager to create my own npm library in ReactJS. Taking inspiration from my existing React project, the goal is to transform it into a package (or library) that can be easily integrated into other projects. This means allowing ...

Tips for implementing self-managed state in Vue.js data object

My approach in organizing my Vue application involves using classes to encapsulate data, manage their own state (edited, deleted, etc), and synchronize with the back-end system. However, this method seems to conflict with Vue in some respects. To illustra ...

What is the correct method for importing a Node module into Angular using TypeScript or AngularCLI?

As I integrate some "legacy" (non-typescript) JavaScript libraries into my Angular single page application. Usually, I simply include a CDN link in the index.html file like this: <script src="//cdnjs.cloudflare.com/ajax/libs/pako/1.0.6/pako.min.js"> ...

Steps for importing a CommonJS module that exports as a callable into TypeScript

I am dealing with a project that has a mixture of JavaScript and TypeScript files. Within the project, there is a JS library that follows this structure: module.exports = () => { // logic dependent on environment variables // then... return { ...

Similar to Java method references, TypeScript also provides a way to reference functions

Although I am familiar with Java, TypeScript is fairly new to me. In Java, lambda expressions (->) or method references (::) are commonly used to satisfy functional interfaces. It seems like lambda expressions function similarly in both languages (plea ...

Error in Template Syntax for External Pug Templates: Component template must have a root element, not just plain text

I've been struggling to make Pug templates work with Vue class-based components using a separate file for the pug template. The documentation suggests that adding this code should do the trick: // webpack.config.js -> module.rules { test: /&bsol ...

What is the easiest way to retrieve a basic date with the month represented by a numerical

Struggling to retrieve the date in the format "Oct 29". I attempted using split but it varies every day. This is what I've come up with so far. let currentDate = new Date().toLocaleDateString('en-US', { month: 'short', day: 'n ...

Tips for retrieving the text from a POST request in C#

I have a basic Angular form that allows users to upload a file along with a description. constructor(private http: HttpClient) { } upload(files) { if (files.length === 0) return; const formData: FormData = new FormData(); var filedesc ...

What is the syntax for passing a generic type to an anonymous function in a TypeScript TSX file?

The issue lies with the function below, which is causing a failure within a .tsx file: export const enhanceComponent = <T>(Component: React.ComponentType<T>) => (props: any) => ( <customContext.Consumer> {addCustomData => ...

What is the most efficient way to minimize the use of if statements in an Angular component when calling a specific function?

Currently, I am working on an Angular 7 temperature conversion application. Within my formGroup, there are inputs and outputs along with two multi-select dropdowns where users can choose the unit of temperature 'From' and 'To' for conve ...

Unable to run unit tests on project using my custom React library

If any of you have encountered this issue or know how to solve it, please help me. I created an NPM package that can be found at https://www.npmjs.com/package/@applaudo/react-clapp-ui It installs and runs smoothly in other projects using create react app; ...

The node.js command runs successfully in the terminal, however, it encounters issues when executed

Something strange is happening in my project. After updating all the development dependencies, my dev:server script stopped working. Now, when I try to run it using npm or yarn, I encounter the following error: npm run dev:server > <a href="/cdn-cg ...

Ionic - Deleting an item from local storage

Currently, I am implementing local storage for my Ionic application. While I can successfully add and retrieve data from local storage, I encounter an issue when trying to delete a specific object - it ends up removing everything in the database. Moreover, ...

How to implement and utilize a history-object interface in React with Typescript?

Can you help me with setting up an interface for a history object in my component? Currently, it is typed as any and I want to type it appropriately. Object: https://i.sstatic.net/Sru8R.png Here's the code snippet: import React, { useState } from &a ...