Different types that are not interchangeable within a function declaration

Can conditional types in TypeScript achieve the following scenario?

type Type1 = {
  field: string,
}

type Type2 = {
  field: number,
}

// Ensuring that arg1 and arg2 are either both Type1 or both Type2
const func = (arg1: Type1 | Type2, arg2: Type1 | Type2) {
  // do something
}

Answer №1

To tackle this issue, one approach could be to overload the signature and get it over with.

One possible method is to utilize a union of tuples:

const func = (...args: [arg1: Type1, arg2: Type2] | [arg1: Type2, arg2: Type1]) => {
    const arg1 = args[0] 
      // Type1 | Type2
    const arg3 = args[2] 
      // Property '2' does not exist on type ...
    return;
}

func({field: 32}, {field: "hi"}) 
  // ok
func({field: "hi"}, {field: 32}) 
  // ok
func({field: 32}, {field: 2}) 
  // Type at position 1 in source is not compatible with type at position 1 in target.

This can yield detailed error messages, but the readability of the function signature may suffer.

Alternatively, you can follow the conditional route as proposed in your initial query:

type Exclusive<A, B, T1, T2> = 
    A extends T1
        ? B extends T2
            ? A : never 
    : A extends T2
        ? B extends T1
            ? A : never
    : never;

type ExclusiveTypes<A, B> = Exclusive<A, B, Type1, Type2>

const func = <T, U>(arg1: ExclusiveTypes<T, U>, arg2: ExclusiveTypes<U, T>) => {
    return;
}

func({field: 32}, {field: "hi"}) 
  // ok
func({field: "hi"}, {field: 32}) 
  // ok
func({field: 32}, {field: 2}) 
  // Type 'number' is not assignable to type 'never'.

The signature here might be even more obscure, and the error message might not provide much insight. It can be perplexing to encounter a parameter being labeled as never.

Another option is to employ overloads:

function func(arg1: Type1, arg2: Type2): void;
function func(arg1: Type2, arg2: Type1): void;
function func(arg1: Type1 | Type2, arg2: Type1 | Type2) {
    return
}

func({field: 32}, {field: "hi"}) 
  // ok
func({field: "hi"}, {field: 32}) 
  // ok
func({field: 32}, {field: 2})
  // No overload matches this call.

Using overloads remains the conventional way to manage polymorphic function signatures in TypeScript.

Answer №2

A different approach is presented here without the use of overloads or similar to conditional types by @lawrence-witt.

function func<A extends Type1 | Type2>(arg1: A, arg2: Exclude<Type1 | Type2, A>) { ... }

This method involves retaining the type of the first argument through a generic parameter and then excluding that type from the second argument, leaving only the other possibilities.

Interactive example available here

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

Filter the output from a function that has the ability to produce a Promise returning a boolean value or a

I can't help but wonder if anyone has encountered this issue before. Prior to this, my EventHandler structure looked like: export interface EventHandler { name: string; canHandleEvent(event: EventEntity): boolean; handleEvent(event: EventEntity ...

Styling with CSS in Angular 2+ can be quite challenging

Hey there, I'm new to Angular 4 and running into some troubles with styling my application. I tried adding a background image to the body, which worked fine, and then added a component to display content, also looking good. Now, when I added a second ...

Is it possible to dynamically name keys in objects using template literals in Typescript?

Can the scenario below be achieved? type test = <T extends string>(key: T, object: { [`${T}`]: number }) => void ^^^^^^^^ I am aware that we can assign type literal values using that syntax, but af ...

Using Typescript: What is the best way to convert a variable into a specific element of an array?

List of Strings: const myStrings = ["one", "two", "three"]; const newString = "two"; The variable newString is currently just a string, but I would like its type to be an element of myStrings. Is there a way to achi ...

React Native is throwing a TypeError because it is encountering an undefined object

React Native is throwing an error claiming Undefined is not an object when it's clearly an object!! I'm confused about what's happening. Take a look at the code snippet below. Scroll down to the render() function. You'll see the follow ...

A different approach to handling multiple constructors in Angular 4

Angular 4 does not support having multiple constructors, so I need to find a cleaner way to instantiate my object. This is what my model looks like: export class SrcFilter { constructor(public firstList?: Array<String>, public secondList?: Arra ...

Encountering an issue with applying D3 fill to a horizontal stacked bar chart in Angular using TypeScript. When using .attr("fill", ..) in VSC, an error stating "No overload matches this call" is displayed

My goal is to create a stacked horizontal bar chart in d3, and I've been following the code example provided here. To showcase my progress so far, I have set up a minimal reproduction on stackBlitz which can be found here. While there are no errors ...

How to update nested properties in Typescript using bracket notation

Imagine there is an interface and object with nested properties as shown below: interface Iobj { a: { a2:string }; b: string; } const obj: Iobj = { a:{ a2: "hello" } b: "world" }; Now let's say we have strings that ...

How to send form group in Angular when the enter key is pressed

When I press the submit button on a form, it sends a request to the database to filter data in a grid. However, I also want the form to submit when the enter key is pressed. HTML <form [formGroup]="bmForm" (keyup.enter)="onSearchClic ...

Visual Studio Code is encountering issues when trying to start a Node application

I am in the process of setting up a workflow for an express app using TypeScript, Visual Studio Code, and gulp. Here is the structure of my project: src/ <-- source files Start.ts Server.ts models/ Contact.ts Organization.ts bin/ <- ...

The isAuthenticated status of the consumer remains unchanged even after being modified by a function within the AuthContext

I am working on updating the signout button in my navigation bar based on the user's authentication status. I am utilizing React Context to manage the isAuthenticated value. The AuthProvider component is wrapped in both layout.tsx and page.tsx (root f ...

Need help with TypeScript syntax for concatenating strings?

Can you explain the functionality of this TypeScript syntax? export interface Config { readonly name: string readonly buildPath: (data?: Data) => string readonly group: string } export interface Data { id: number account: string group: 'a&a ...

Invoke the API when the value of a property in the SPFX property pane is modified

Here's a question that might sound silly, but I'll ask anyway. I have a dropdown field in my Property pane that is filled with all the lists from the current site. It's working fine. When I change the dropdown selection, it populates a pro ...

Issue with uploading video files using ng2-file-upload in Angular7 and ASP .Net Core 2.1

While working on my project, I encountered an issue with uploading video files using ng2-file-upload to the server. The photo upload functionality is working fine, but when attempting to upload a video file larger than 27MB, the process gets canceled autom ...

The class is not correctly implementing the 'OnInit' interface. The 'ngOnInit' property is missing in the 'Component' type, which is required in the 'OnInit' type

Could someone offer assistance with this issue? It seems like there are errors in the code structure: Class 'ContactFormComponent' incorrectly implements interface 'OnInit'. Property 'ngOnInit' is missing in type 'Contac ...

Creating both Uniform and Varying drawings on a single webGL canvas

My goal is to create this specific illustration. https://i.sstatic.net/5AfdW.png This project requires the usage of TypeScript. The Code: The code is organized across multiple files. Within the scenegraph file, there's a function that visits a gro ...

Jest | Testing Tool for Functions with Multiple Parameters

I have developed a new tool that requires 3 parameters: value (string), service (any - an actual GET service), and name (string). Below is the code for the tool: import { serverErrorResponseUtil } from 'util/serverErrorResponseUtil'; import { H ...

Utilizing lodash and Angular 8: Identifying an valid array index then verifying with an if statement

In my current project, I am developing an e-commerce angular application that includes a basket with two types of products: restaurant + show combos and gift cards. When a client reserves a restaurant, they must also reserve a show; conversely, the client ...

How can models be aggregated in SQL by connecting them through other models?

I am working with a backend express API that utilizes sequelize. In my sequelize models, a Citizen is linked to a Street, which in turn is associated with a Town, and the Town is connected to a State. I can easily count the citizens on a specific Street by ...

Issue discovered: Safari displays a TypeError when a GET request is made to the API server within an Angular 5 application

Currently, I am developing a web app using Angular 5 and facing an issue while trying to call the server's API endpoint. Whenever I receive an error response (400+), it seems that on Safari the app breaks and throws an error. ERROR - TypeError: Type ...