Leveraging default values in generic implementations

Imagine a scenario where the following code is present:

type QueryResult<ResultType = string, ErrorType = string> = {
  result: ResultType,
} | {
  errors: ErrorType,
}

So, if I want to initialize my result, I can proceed like this:

const myResult: QueryResult = {
  result: "My result!",
};

In addition, when I need to modify the ResultType, it can be included in the generic type:

const myResult: QueryResult<number> = {
  result: 10,
  errors: null
};

However, what about changing the type value for errors? One way of achieving this is by doing the following:

// Although functional, specifying the default value is not ideal
const myErrorResult: QueryResult<string, MyErrorType> = {
  errors: {
    location: [1, 2],
    message: "Invalid argument!",
  }
};

Is there a way to skip specifying the ResultType? Is this even feasible? The main motive behind this query is to avoid providing default values for each generic type, especially when dealing with numerous generics. Attempts such as QueryResult<, string> and

QueryResult<ErrorType = MyErrorType>
have resulted in errors.

You can access the playground link here.

Thanks for your assistance!

NOTE: This example serves solely for illustrative purposes, acknowledging that it may not be an optimal representation method for errors/results.

Answer №1

Unfortunately, TypeScript does not natively support skipping over generic type parameters. Ongoing feature requests can be found at microsoft/TypeScript#10571 and microsoft/TypeScript#26242, although the specifics of how skipping should operate are still unclear. As a workaround, you can create a "marker" or "sigil" type, such as Default, to signal fallback behavior.


To implement this workaround, define a marker type Default with a private property:

declare class Default { private prop: string }

The Fallback<T, D> type will return T unless it's Default, in which case it falls back to D:

type Fallback<T, D> = T extends Default ? D : T;

Rename the original QueryResult as _QueryResult and introduce QueryResult using the fallback logic:

type _QueryResult<R, E> = { result: R } | { errors: E };

type QueryResult<R = Default, E = Default> =
  [Fallback<R, string>, Fallback<E, string>] extends [infer R, infer E] ?
  _QueryResult<R, E> : never;

You can now use QueryResult with fallbacks for generic types as needed:

const myResult: QueryResult = { result: "My result!" };

const myGenericResult: QueryResult<number> = { result: 10 }

const myErrorResult: QueryResult<Default, MyErrorType> = {
  errors: {
    location: [1, 2],
    message: "Invalid argument!",
  }
};

This workaround provides a solution, but may not align with expectations around type inference for some use cases. Explore alternatives that better suit your needs, and consider supporting existing feature requests on GitHub if none of the workarounds suffice.


Playground link to code

Answer №2

jcalz's response is excellent, however I have an alternative suggestion to consider.

One simple solution could be to establish a distinct type specifically for this scenario:

type AlternateErrorQueryType<OtherErrorType = string> = QueryType<string, OtherErrorType>;;

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

I am hoping to refresh my data every three seconds without relying on the react-apollo refetch function

I am currently working with React Apollo. I have a progress bar component and I need to update the user's percent value every 3 seconds without relying on Apollo's refetch method. import {useInterval} from 'beautiful-react-hooks'; cons ...

Exploring ways to retrieve the chosen value from a personalized dropdown menu in React?

I'm currently utilizing styled components along with typescript to retrieve the selected option value of a customized dropdown. However, I am encountering an issue where the value does not update as expected. Interestingly, when I remove the opacity f ...

What could be causing the error "Type 'String' cannot be used as an index type" to appear in my TypeScript code?

My JavaScript code contains several associative arrays for fast object access, but I'm struggling to port it to TypeScript. It's clear that my understanding of TypeScript needs improvement. I've searched for solutions and come across sugges ...

A guide on incorporating Typescript into Material UI v5 themes

A similar question has been asked previously, however... I am looking to enhance my color options by adding variants such as success, warning, and more choices within the background category (palette.background). Specifically interested in a lite option t ...

Continue iterating using (forEach, map,...) until the object (children) has no more elements

I need to disable the active status for all elements within my object structure. Here is an example of my object: const obj = { name: 'obj1' , ative: true , children: [ { name: 'obj2' , ative: true , children: ...

Updating non-data properties dynamically in a custom AG Grid cell renderer

In my grid setup, I have implemented an editor button in a column for each row and a new item creator button outside the grid. One of the requirements is that all buttons should be disabled when either the create or edit button is clicked. To achieve thi ...

To implement a filter in MongoDB, make sure to specify a function argument before

Utilizing TypeScript, Node.js, Mongoose, and MongoDB in my project. I have a function that resembles the following: async function getAllBooks(title?: string, authorName?: string, sortBy?) { const books = await bookModel.find().sort(); return book ...

Learn how to generate specific error messages based on the field that caused the failure of the @Column({ unique: true }) Decorator. Error code 23505

Hey there! I'm currently facing an issue while trying to handle Sign Up exceptions in my code. I want to inform the user if their username OR email is already in use. Although using the decorator @Column({ unique: true}) allows me to catch error 23505 ...

What is the purpose of adding "/dist" to the library import statement?

Currently, I am in the process of developing a react component library using vite as my primary build tool. After successfully compiling the project and deploying it to the npm registry, I encountered an issue when importing it into my client app. Specifi ...

Extend the express request object with Typescript and then export the modified object

Seeking to enhance the Request object from express with custom fields using typescript. Based on this particular source, I created a file named @types/express/index.d.ts containing the following code : import { MyClass } from "../../src/MyClass" ...

How to make an optional prop with a default value non-nullable in a ts+react component?

Is there a way to modify a React component to accept an optional prop and then treat it as non-null within the component itself? For example, consider the following basic component: import React from 'react'; type props = { x?: number; }; c ...

I am interested in adding a personalized icon to the progress bar in Material-UI

I am currently using the MUI linerProgressBar design. I would like to incorporate a custom UI Icon that moves along with the progress. Are there any examples of this available? I have searched for one in MUI but haven't found anything. If you know of ...

Address aliases in the webpack configuration file

When utilizing webpack, it is possible to write the configuration file using TypeScript. However, it is crucial to ensure that any alias paths present in the config file are resolved to their mapped paths. It should be noted that this pertains specificall ...

Here's how you can combine several elements from one array and assign them to the first element of another array:

I'm working with an array that looks like this: public static readonly List: Array<any> = [ { name: 'CCS', link: 'Dummy link1' }, { name: 'CCR', link: 'Dummy link2' }, { name: 'PM', ...

Integrating Auth0-js with the usePostMessage functionality

Encountering difficulties when compiling an Angular application that incorporates the auth0-js package. The code utilizes the method renewAuth(options: RenewAuthOptions, callback: Auth0Callback<any>): void;, yet it seems to be causing issues as the p ...

Tips for maintaining the selection when switching pages with smart-table?

I have implemented smart-table in my project to allow users to select records from different pages and view them in a preview section. However, I am facing an issue where the selection made on the first page does not persist when navigating back to it aft ...

Guide on toggling mat-checkbox according to API feedback in Angular 6

Just starting out with angular 6 and I'm attempting to toggle the mat-checkbox based on the API response. However, I seem to be having trouble. All the checkboxes are showing as checked even when the API response is false. <div class="col-sm-12" ...

The View does not get updated by Angular's *ngFor directive

When I modify the Array of servers from outside the class declaration, the View/HTML component does not update accordingly. However, when I perform the same modification from inside the class, it works fine. Both functions successfully update the servers A ...

The absence of essential DOM types in a TypeScript project is causing issues

Recently, I've been working on setting up a web app in TypeScript but I seem to be missing some essential types that are required. Every time I compile using npm run build, it keeps throwing errors like: Error TS2304: Cannot find name 'HTMLEleme ...

Retrieve user information by their unique user ID from a MongoDB database using a Node, Express, and TypeScript API

Currently, I am working on a Node JS and Express with TypeScript API project. In this project, I need to retrieve data stored by a specific user from MongoDB based on their user ID. This is a snippet from my DataRouter.ts: router.get('/:userId', ...