Conditional void parameter type in Typescript

Attempting to create a custom Error class that can handle different data based on the error code seemed like a complex task for TypeScript. However, surprisingly, it was successful:

const enum ERROR_CODES {
  E_AUTHORIZATION = 'Authorization error',
  E_REQUEST = 'Request failed',
  E_INVALID = 'Invalid data',
}
interface ERROR_TYPES {
  [ERROR_CODES.E_AUTHORIZATION]: string
  [ERROR_CODES.E_REQUEST]: { url: string; status: number }
  [ERROR_CODES.E_INVALID]: { action: string; field: string }
}

class MyError<TError extends ERROR_CODES> extends Error {
  constructor(
    public readonly code: TError,
    public readonly data: ERROR_TYPES[TError],
  ) { super(code) }
}

Usage example:

throw new MyError(ERROR_CODES.E_AUTHORIZATION, 'whatever')
throw new MyError(ERROR_CODES.E_AUTHORIZATION, { 
  operation: 'login',
  field: 'email',
})

The above examples work as expected. Creating error codes that do not require any data led me to this:

const enum ERROR_CODES {
  // ...
  E_UNKNOWN = 'Unknown error'
}
interface ERROR_TYPES {
  // ...
  [ERROR_CODES.E_UNKNOWN]: void
}

Interestingly, TypeScript behaves unexpectedly when attempting to create an error with just one argument:

throw new MyError(ERROR_CODES.E_UNKNOWN, undefined)

This works. However, if we try this:

throw new MyError(ERROR_CODES.E_UNKNOWN)

An error Expected 2 arguments, but got 1. is thrown. Even using void 0:

throw new MyError(ERROR_CODES.E_UNKNOWN, void 0)

Which should be equivalent to the first example, results in

Expected 'undefined' and instead saw 'void'.
. What exactly is causing this behavior and is there a way to make the second example work with just one argument?

Answer №1

I encountered an issue where I am seeing "void" instead of the expected result "undefined".


To address making a function parameter optional, I suggest representing the parameter list as a rest tuple. You can create a type alias like this:

type UndefParamToOptional<T> = undefined extends T ? [T?] : [T];

For example,

UndefParamToOptional<string>
results in [string], while
UndeParamToOptional<string | undefined>
yields [string?]. This allows for creating a tuple with an optional element corresponding to an optional function parameter. The implementation of MyError would look like this:

class MyError<TError extends ERROR_CODES> extends Error {
  constructor(
    code: TError,
    ...[data]: UndefParamToOptional<ERROR_TYPES[TError]>
  );
  constructor(public readonly code: TError, public readonly data: ERROR_TYPES[TError]) {
    super(code);
    this.data = data;
  }
}

A single overload signature is used to illustrate how the function should be called, while keeping the implementation signature consistent.

This adjustment should provide the desired behavior:

throw new MyError(ERROR_CODES.E_UNKNOWN); // works fine

Hope this explanation proves helpful. Best of luck!

Link to code

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

Is there a solution available for the error message that reads: "TypeError: Cannot set value to a read-only property 'map' of object '#<QueryCursor>'"?

Everything was running smoothly in my local environment, but once I deployed it on a Digital Ocean Kubernetes server, an error popped up. Any assistance would be greatly appreciated. https://i.stack.imgur.com/VxIXr.png ...

PrimeNG's p-table in Angular is not being recognized when using @ViewChild

Here is the code snippet I am referring to: import { DataTable } from 'primeng/primeng'; @Component({ moduleId: module.id, templateUrl: 'search.component.html' }) export class SearchComponent { @ViewChild(DataTable) pr ...

Exploring the capabilities of using the injectGlobal API from styled-components in a TypeScript

I've been attempting to utilize the simple injectGlobal API, but I am encountering difficulties getting it to work with TypeScript. Here is my setup in theme.tsx: import * as styledComponents from "styled-components"; import { ThemedStyledComponentsM ...

Setting up NextJs in Visual Studio Code with Yarn

When I used yarn create next-app --typescript to set up a TypeScript Next.js application with Yarn, everything seemed to be working fine with the command yarn run dev. However, Visual Studio Code was not recognizing any of the yarn packages that were added ...

Error 404: Angular 2 reports a "Not Found" for the requested URL

I am currently in the process of integrating an Angular 2 application with a Java Spring Boot backend. As of now, I have placed my Angular 2 files under src/main/resources/static (which means that both the Angular and Spring apps are running within the sam ...

Mocking a named class-export in TypeScript using Jest

I have a node module that exports several classes, including one called Client, which I utilize to create clients with various APIs as methods. I'm currently attempting to test my module, which has a dependency on this node module, using Jest. Howeve ...

Replace Formik with useFormik to streamline your code

I have implemented Formik/Yup for validation on a page that triggers a GraphQL mutation. The code is functioning as expected: export default function RemoveUserPage() { const [isSubmitted, setIsSubmitted] = useState(false); const [isRemoved ,setIsRemo ...

Dealing with Errors in Node.js Using Typescript and Express

As I embark on my journey with Node and Typescript, a question has cropped up in my mind. There are two key files in my project: server.ts import express = require('express'); import IConfiguration from "../config/config"; export default clas ...

Ensuring the correctness of environment variables in Next.js using Zod

After spending the entire day trying to figure it out, I realize that the solution may be simpler than expected. I am currently using the well-known zod library to validate my environment variables and transform data. However, I keep encountering a persis ...

What is the best way to eliminate square brackets from keys within an array of objects in TypeScript?

I am currently working on a task to eliminate all square brackets from the keys in the entries field within an array of objects: data: [ {title: "Title1", entries: { 'Entry1': 333333, '[ABC]Entry2': 1234, 'Entry3' ...

Angular 4 allows you to assign unique colors to each row index in an HTML table

My goal is to dynamically change the colors of selected rows every time a button outside the table is clicked. I am currently utilizing the latest version of Angular. While I am familiar with setting row colors using CSS, I am uncertain about how to manip ...

An error occurred: The property 'toUpperCase' cannot be read of an undefined Observable in an uncaught TypeError

Encountering an issue during the development of a mobile app using the ionic framework for a movie application. After finding an example on Github, I attempted to integrate certain functions and designs into my project. The problem arises with the 'S ...

Angular8 with the [tinymce] library for customizing editor elements and configuring multiline options

I am currently working with Angular 8 and in the template, we have the following code snippet: <editor required class="research-modal__control__input research-modal__control__input__description" formCo ...

Steps for creating a dynamic validation using a new form control

I have an item that needs to generate a form const textBox = { fontColor: 'blue', fontSize: '18', placeholder: 'email', name: 'input email', label: 'john', validation: { required: false } ...

Guide to utilizing props conditionally in a Material UI TextField component

I am looking to build a comprehensive component using Material UI TextField along with Formik While I have managed all other props, I am facing difficulty in dealing with value and onChange, I have tried using 2 functions for onChange, but find it cumber ...

Is there a way to ensure DRY principles are followed while utilizing Redux Toolkit's asyncThunkCreator?

I have a query related to RTK. I find myself repeating code in my action creators created with createAsyncThunk because I want to be able to cancel any request made. I am thinking of creating a wrapper to streamline this process, but I am facing challenge ...

Resolve the error message "Class is not a constructor" which arises while utilizing namespaces in TypeScript with WebPack

Issue with TypeScript Namespaces in PIXI.js As I delve into the world of TypeScript and PIXI.js without any framework, I find myself facing a challenge. Previously, at work, we utilized namespaces with the module keyword for a cleaner structure. However, ...

Having trouble loading extensive amounts of data into a select element within an Angular application

Upon successfully retrieving around 14000 data entries from an HTTP request, I am facing difficulties loading this vast amount of data into my Select Tag. This is causing the entire page to slow down. The structure of the select Tag in question is as follo ...

Having trouble with typecasting in Angular 9 after receiving an HTTP response?

When initializing my component, it fetches student information from an API. Here is the ngOnInit code for component-version1: ngOnInit(): void { if(!this.student) { this.studentsService.getStudentDetail(this.id).subscribe( (response: Stu ...

Patience is key as you await the completion of an API call in Angular 14

Within my Angular 14 application, I am faced with a scenario where I need to make two API calls and then combine the results using "combineLatest" from rxjs. The combined data is then assigned to a variable that I must use in a separate function. How can I ...