Using TypeScript to assert the type of a single member in a union of tuples, while letting TypeScript infer the types of the other members

Currently, I am attempting to implement type assertion for the "error-first" pattern.

Within my function, it returns tuples in the format of ['error', null] or [null, 'non-error'].

The specific condition I want to check for is error === null, with TypeScript assuming that the second element is not-null.

To illustrate this further, consider the following example:

type ErrorMessage = string;
type ResultError = [ErrorMessage, null];
type ResultOk<T> = [null, T];
type Result<T> = ResultError | ResultOk<T>;

interface I {
    x: number;
}
function f(): Result<I> {
    if (Math.random() > 0.5) {
        return [null, {x: 1}];
    } else {
        return ['my-error', null];
    }
}

const [error1, result1]: Result<I> = f();

// Current approach, manually checking if result1 is not null...
if (result1 !== null) {
    console.log(result1.x);
}

// Desiring a different method where only error being null is checked,
// and result1 is automatically assumed as type I
if (error1 === null) {
    console.log(result1.x); // TS2531: Object is possibly 'null' <<=====
}

Answer №1

To help TS narrow down the type, we can utilize type guards. Take a look at the code snippet below:

const isOk = <T>(r: Result<T>): r is ResultOk<T> => r[0] === null; // defining a type guard

const result: Result<I> = f(); // result can be either Ok or Error

if (isOk(result)) {
  const [error1, result1] = result; // within this block, result is of type ResultOk
}

Additional information - while you could achieve similar functionality using a standard if statement like r[0] !== null, creating a named type guard for a custom structure is considered a more preferred approach.

Answer №2

Your code is perfectly aligned with the expected behavior.

Just a minor tweak: Avoid using destructuring to declare the results variable; instead, treat it as a single value:

const result: Result<I> = f();

if (result[0] === null) {
  const {x} = result[1]
  console.log(x); // works fine
}

This approach works as intended without requiring any additional functions or type guards.

A shoutout to this insightful answer that delves into why TypeScript behaves this way: Narrow type based on conditional type(Typescript)

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

Angular POST sends null to .NET Core API

I've been encountering an issue while trying to send data from Angular to my .NET Core API, as the received data always ends up being null. Take a look at my code: Here is the Angular POST request: public insertCategory(categoryToInsert: ICategoryDTO ...

Using TypeScript with React: Automatically infer the prop type from a generic parameter

I've developed a component that retrieves data from an API and presents it in a table. There's a prop that enables the fetched value to be displayed differently within the table. Here's how I currently have the prop types set up: // T repres ...

What is the best way to store the output of a function in a local variable?

In my Type Script code, I am looking to store the return value of a function in a local variable. The process is outlined below: getdetail1(store){ let Cust_id=this.sharedata.latus_lead.m_type let url="http:domain.com" console.lo ...

Tips for altering a key within a tree-view:

I am working with a potentially infinite tree-view array: type Tree = { id: number; name: string; email: string; children: Tree[]; }; const tree: Tree[] = [ { id: 1, name: 'Truck', email: '@mail', children ...

Utilize dynamic components to load varying data sets multiple times

Is there a way to dynamically load a component multiple times and pass data based on certain values so that it will display with real-time information? I came across an example at the following link: In this example, there is a messageComponent with a "m ...

Issues with slider functionality in a Next.js application styled with Tailwind CSS

"use client"; import React, { useState } from "react"; const textData = [ { id: 1, text: "Text 1 Description", }, { id: 2, text: "Text 2 Description", }, { id: 3, text: "Text 3 ...

showcasing products from database with the help of Angular 12

Here are the files related to the item: Item file And here is the component file: Component file Lastly, this is the data service file: Data Service file However, issues arise when testing the code with console log statements as it indicates that the ...

The error message stating that property 'catch' does not exist on type 'Observable<IEmployee[]>' cannot be fixed by simply adding the import 'rxjs/add/operator/catch'

When I hover over .catch(this.errorHandler), an error message is displayed Property 'catch' does not exist on type 'Observable'.ts(2339) I am unable to import the catch function into Angular Typescript. Upon hovering over .catch(th ...

Enable Intellisense for my custom ES6 JavaScript modules in VS Code

Using VS Code Intellisense can greatly enhance productivity when working with internal project files, providing helpful autocompletion features and utilizing written JSDoc comments. However, my current projects involve custom JavaScript libraries stored i ...

Establishing the parameters for a list that is not empty using a specific data type

Is it feasible to establish a non-empty list within TypeScript's type system? While I am aware that it is possible to define a list with a specific number of elements, similar to a Tuple: type TwoElementList = [number, number]; This approach is limi ...

Exploring ways to display all filtered chips in Angular

As a new developer working on an existing codebase, my current task involves displaying all the chips inside a card when a specific chip is selected from the Chip List. However, I'm struggling to modify the code to achieve this functionality. Any help ...

Exploring how NestJS can serialize bigint parameters within DTOs

I have data transfer objects (DTOs) with parameters that are of type bigint. However, when I receive these DTOs, the parameters always have a type of string. Here is an example: @Get("") async foo(@Query() query: Foo) { console.log(typeof Foo ...

The getStaticProps() function in NextJS has not been invoked

Currently, I am working on a basic website and my goal is to retrieve data from an API and showcase it on my component. However, I've encountered an issue where the getStaticProps() method doesn't seem to be triggering. Below is the code snippet ...

Angular 2+ encountering an internal server error (500) while executing an http.post request

Here is my service function: public postDetails(Details): Observable<any> { let cpHeaders = new Headers({ 'Content-Type': 'application/json' }); let options = new RequestOptions({ headers: cpHeaders }); return this.htt ...

What is the best way to determine the final letter of a column in a Google Sheet, starting from the first letter and using a set of

My current approach involves generating a single letter, but my code breaks if there is a large amount of data and it exceeds column Z. Here is the working code that will produce a, d: const countData = [1, 2, 3, 4].length; const initialLetter = 'A&a ...

Issues with Angular displaying filter incorrectly

Whenever a user chooses a tag, I want to show only the posts that have that specific tag. For instance, if a user selects the '#C#' tag, only posts with this tag should be displayed. This is how my system is set up: I have an array of blogs that ...

In Angular 8, a communication service facilitates interaction between parents and children

Using a Sharing service, I am able to pass data from my app component to the router-outlet render component. While I have been successful in passing strings and other data, I am now looking for a way to retrieve data from an API and share it with a compone ...

Unable to initialize the ng2-admin Angular2 Bootstrap template on a Go server due to a failed download attempt

Hello everyone, I am a newcomer to this forum and have limited experience with Angular2 and Golang. I am currently facing an issue as I want to experiment with a template on a Go server. To tackle this, I created a main.go file that includes the followin ...

node-ts displays an error message stating, "Unable to locate the name '__DEV__' (TS2304)."

I recently inserted __DEBUG__ into a TypeScript file within my NodeJS project. Interestingly, in VSCode, no error is displayed. However, upon running the project, I encounter an immediate error: error TS2304: Cannot find name '__DEBUG__'. I att ...

Trouble arises when trying to open a new window using the Angular 8 CDK

I am attempting to open my component in a new window, similar to this example: https://stackblitz.com/edit/angular-open-window However, when the window opens, my component is not displayed and I receive the following error in the console: Error: Must pro ...