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

What is the best approach for managing optional object input parameters while also verifying the presence and accuracy of that specific property?

What is the best approach to handling a situation where a method has optional object member properties for the options object, but you still want to ensure the presence of that property with a default value in the resulting instance? Is creating a separate ...

Having trouble getting tsserver-plugins to function properly in either Atom or VSC

My team and I are on a mission to enhance our Angular 2 templates with code completion, similar to what is showcased in this gif. To achieve this goal, we require: tsserver-plugins coupled with tslint-language-service and @angular/language-service We ...

How to retrieve a Typescript variable within an ngIf conditional statement in Angular

How can I access a TypeScript variable inside an ngIf statement? TS: public data: string; HTML: <td> <div *ngIf="data === 'BALL' ; else noplay" >{{ play }}</div> <ng-template #noplay> {{ gotohome ...

How can I access a TypeScript variable from a global var set in a .cshtml file?

I have defined several variables in my .cshtml file to be used in my angular services: <script> var _APIURL = '@(Url.Content("~/Admin/api/"))'; var _AREAURL = '@(Url.Content("~/Admin/"))'; var _APPURL = &a ...

Is there a workaround for utilizing reducer dispatch outside of a React component without relying on the store?

Recently, I implemented a reducer in my project that involves using react, typescript and nextJS. I am wondering if there is a method to trigger the reducer outside of a react component, such as from an API service. While searching for solutions, most re ...

The Angular-Chart.js chart fails to display when data is automatically inserted

I came across this sample code at https://stackblitz.com/edit/angular-chartjs-multiple-charts and tried using it. Everything was working well with static data, but when I attempted to push data retrieved from the Firebase Realtime Database into the chart, ...

The type 'number' cannot be assigned to the type 'Element'

Currently, I am developing a custom hook called useArray in React with TypeScript. This hook handles array methods such as push, update, remove, etc. It works perfectly fine in JavaScript, but encounters errors in TypeScript. Below is the snippet of code f ...

What is the best way to specify a function type that takes an argument of type T and returns void?

I am in search of a way to create a type that can accept any (x: T) => void function: let a: MyType; a = (x: number) => {}; // (x: number) => void a = (x: string) => {}; // (x: string) => void a = (x: SomeInterface) => {}; / ...

How to Position Logo in the Center of MUI AppBar in React

I've been struggling to center align the logo in the AppBar. I can't seem to get the logo to be centered both vertically and horizontally on its own. Here is my code from AppBar.tsx: import * as React from 'react'; import { styled, useT ...

Use Angular's super.ngOnDestroy method for handling cleanup/unsubscribing

When it comes to unsubscribing / cleaning up from observables in Angular components (using ngOnDestroy), there are multiple options available. Which option should be considered the most preferable and why? Also, is it a good practice to include super.ngOnD ...

Can a TypeScript generic version of the Y-combinator be successfully executed?

Here is an interesting JavaScript implementation of the Y-combinator: const Y = g => (x => g(x(x)))(x => g(x(x))) //or const Y = f => { const g = x => f(x(x)) return g(g) } I've been thinking, could it be possible to create a TypeS ...

The art of crafting informative error log messages in Protractor using TypeScript

I am currently utilizing Protractor, written in typescript, to conduct tests on a live website. I am seeking guidance on how to generate log messages when a Protractor test fails. Presently, the only feedback received is a simple YES/NO message, as shown b ...

Choose an option from a selection and showcase it

I need to implement a modal that displays a list of different sounds for the user to choose from. Once they select a sound, it should be displayed on the main page. Here is the code snippet for the modal page: <ion-content text-center> <ion-ca ...

The ngFor directive in Angular should be used with the Filter pipe to ensure that

Having a Filter implemented in my Angular Project that fetches data from Firebase. The current status in the Filter is as follows: Name 1: Lea Muster Name 2: Bruno Mustermann Name 3: Lea Muster Name 4: Gabriela Musterfrau The goal is to show duplicate e ...

Tips for utilizing RouterLink within an HTML template

Angular 2.3.0 I created a module in Angular 2 as shown below: import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { RouterModule, Routes } from '@angular/router'; impo ...

What is the process for generating an object type that encompasses all the keys from an array type?

In my coding journey, I am exploring the creation of a versatile class that can define and handle CRUD operations for various resources. The ultimate goal is to have a single generic class instance that can be utilized to generate services, reducer slices, ...

The process of incorporating types into Node.js and TypeScript for handling req and res objects

Check out the repository for my project at https://github.com/Shahidkhan0786/kharidLoapp Within the project, the @types folder contains a file named (express.d.ts) where I have added some types in response. The (express.d.ts) file within the @types folde ...

Tips for importing a .geojson document in TypeScript using webpack?

I am trying to extract data from a .geojson file but faced some challenges while attempting two different methods: const geojson = require('../../assets/mygeojson.geojson'); The first method resulted in an error message stating: Module parse f ...

Utilizing Office.js: Incorporating Angular CLI to Call a Function in a Generated Function-File

After using angular-cli to create a new project, I integrated ng-office-ui-fabric and its dependencies. I included in index.html, added polyfills to angular.json, and everything seemed to be working smoothly. When testing the add-in in Word, the taskpane ...

Inactive function

I have a function that inserts my articles and I call this function on my page. There are no errors, but the next function retrieveAllArticles() is not being executed. public saveAllArticles(article) { for(let data in article) { this.db.exec ...