A conditional type used with an array to return either an Error object or a generic type when the array is destructured

Within my Typescript project, I've implemented a Result type to be returned from functions, containing either an error or some data. This can take the form of [Error, null], or [null, Data]. Here's an example:

type Result<Data> = [ Error | null, Data | null ]

function randomSuccess(): Result<string> {
    if ( Math.random() > 0.5 ) {
        return [ null, 'success' ]
    else {
        return [ new Error( 'oh no' ), null ]
    }
}

const [ err, result ] = randomSuccess()
if ( err ) {
    .... // now able to handle the extracted error and result

I aim for Typescript to validate that only one of Error or Data is ever not null. For instance:

    ...
        return [ new Error( 'oh no' ), 'success' ]

should trigger an error.

My initial strategy to define a type using conditional types was as follows:

type Result<Data> = null extends Error ? [ null, Data ] : [ Error, null ]

This configuration compiles successfully when the function returns an error. However, when returning valid data - like return [ null, 'success' ] - the compiler raises an issue:

Type 'null' is not assignable to type 'Error'

While I comprehend the compiler error to some extent, it seems like Error lacks a parameter in my type definition, causing null extends Error to always be false. Yet, finding the next steps proves challenging for me.

How do I devise a type that ensures validity is maintained within either [Error, null] or [null, Data], while never allowing [Error, Data]?

Answer №1

If you're in need of a union type, consider creating one where the union consists of two tuples (arrays) representing the two potential states:

type Result<Data> =
    [ null, Data ]      // Success
    |
    [ Error, null ];    // Failure

A Result<Data> can only have one form at a time and must not contain null in both positions or have Error, Data. It also needs to maintain a fixed length of 2.

Here's an illustrative example for better understanding:

type Result<Data> =
    [ null, Data ]      // Success
    |
    [ Error, null ];    // Failure
    
function random(): Result<Date> {
//                        ^^^^−−−− Using `Date` as the `Data` type here just for illustration
    if (Math.random() < 0.5) {
        // Return failure
        return [new Error(), null];
    }
    // Return success
    return [null, new Date()];
}

const a = random();
if (a[0]) {
    // The TypeScript now recognizes that `a[0]` is an `Error`
    console.log(`Error: ${a[0].message}`);
} else {
    // The TypeScript now knows that `a[1]` denotes a `Date`
    console.log(`Success at ${a[1].toString()}`);
}

Explore this concept with TypeScript Playground

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

Activating Ionic6 Stack Modal through JavaScript or TypeScript

Is it possible to trigger the modal using script code instead of a button? I have searched through various examples in the tutorial, but all of them rely on the modal trigger mechanism. <ion-button id="open-modal" expand="block">O ...

What is the best way to interact with the member variables and methods within the VideoJs function in an Angular 2 project

Having an issue with accessing values and methods in the videojs plugin within my Angular project. When the component initializes, the values are showing as undefined. I've tried calling the videojs method in ngAfterViewInit as well, but still not get ...

Accessing a variable within a function in Angular

Recently I started working with Angular and encountered an issue while trying to access a variable inside a function. Below is the code snippet that's causing me trouble: mergeImages() { var imgurl; var canvas: HTMLCanvasElement = this.canv ...

Enclose this within Stencil.js components

Is there a more efficient way to utilize a nested "this" in a Stencil.js component? Currently, I find myself following this approach: render() { let thisNested = this; return <Host> {this.images ? this.imagesArray.map(fu ...

Tips for delivering a variable to a React Native Stylesheet

Is there a way to pass a variable to the "shadowColor" property in my stylesheet from an array declared in the code above? I keep encountering a "Can't find name" error. Attempting to use a template literal has not resolved the issue. Any assistance w ...

Struggling to extract the hours and minutes from a date in IONIC: encountering an error stating that getHours is not a recognized

I encountered an issue while trying to extract the hours and minutes from a date in Ionic. Below is the code snippet from my .html file : <ion-datetime displayFormat="HH:mm" [(ngModel)]='timeEntered1' picker-format="h:mm"></ion-date ...

Toggle the visibility of a dropdown menu based on the checkbox being checked or unchecked

One challenge I am facing involves displaying and hiding DropDown/Select fields based on the state of a Checkbox. When the checkbox is checked, the Dropdown should be visible, and when unchecked, it should hide. Below is the code snippet for this component ...

How can I silence the warnings about "defaultProps will be removed"?

I currently have a next.js codebase that is experiencing various bugs that require attention. The console is currently displaying the following warnings: Warning: ArrowLeftInline: Support for defaultProps will be removed from function components in a futur ...

Bring in Event Types from React using TypeScript

Is there a way to import Event Types from React and use them in Material-ui? Specifically, I am looking for guidance on how to import KeyboardEvent so that it can be utilized for onKeyDown callback type annotation. I have examined the .d.ts file of Mater ...

Angular Material's dialog modal swiftly closes without delay

Could you please explain why the modal opens and then closes instantly when I click on the Create Project button? https://example.com/edit/angular-code I am trying to display a component within the modal using Angular Material. portafolio.component.ts ...

Whenever I update the values in my input using the ngModel attribute, all of my arrays stored in an object also get updated

I am currently working with a table on the frontend. <table class="table table-hover"> <thead> <tr> <th> Account Entry Number </th> <th> ...

Leveraging the ngFor local variable within nested elements

Is there a way to properly display a property of the local variable theme, such as theme.name? Below is an example of how my *ngFor is structured: <ul> <li *ngFor="#theme of themes"> <span>theme.name</span> </li> ...

Issue TS2322: The type 'Observable<any>' cannot be matched with type 'NgIterable<any> | null | undefined'

Encountering an error while attempting to fetch data from the API. See the error image here. export class UserService { baseurl: string = "https://jsonplaceholder.typicode.com/"; constructor(private http: HttpClient) { } listUsers(){ //this ...

What's the trick to aligning the label on the material-ui text field to the right side?

I am working on a React project with an RTL layout using material-ui and typescript. I am struggling to align the label of a text field to the right. Can anyone help me with this? ...

How can I dynamically insert various FormGroup instances into a FormArray in Angular?

I am looking to dynamically populate the order array with multiple dishes. Each dish will be stored as a FormGroup in the form, such as pizza, salad, drink, or any other type of dish. Prior to adding any items, the form structure should resemble this: this ...

Is there a way to verify in Angular whether an image link has a width and height exceeding 1000?

I'm currently working on a function that checks if an image linked in an input field has a width and height greater than 1000 pixels, and is in JPG format. Here's my approach: HTML: <input (change)="checkValidImage(1, product.main_photo)" [ ...

The function cannot be called on a type that does not have a callable signature. The specified type, 'number | Dispatch<SetStateAction<number>>', does not have any compatible call signatures

Currently, I am working on setting up state to be passed through context in React using hooks. However, when I attempt to use the dispatched state updater function, an error is thrown: Cannot invoke an expression whose type lacks a call signature. Type &a ...

The function does not throw a compiler error when a parameter is missing

Currently utilizing TSC Version 2.4.2 Please take note of the following interface: interface CallbackWithNameParameter { cb: (name: string) => void } This code snippet: const aCallback: CallbackWithNameParameter = { cb: () => {} }; Manages t ...

How come the information I receive when I subscribe always seems to mysteriously disappear afterwards?

I've been working on a web project using Angular, and I've run into an issue with my code that's been causing problems for a while now. The problem lies in fetching data from a server that contains translations: getTranslations(): Observab ...

Strategies for implementing classes in Typescript

I've been working on incorporating more classes into my project, and I recently created an interface and class for a model: export interface IIndexClient { id?: number; name?: string; user_id?: number; location_id?: number; mindbody_id?: nu ...