Utilizing union type return values in Typescript

Looking to incorporate shelljs (via DefinitelyTyped) into my Typescript 1.5-beta project. I want to utilize the exec function with the specified signature:

export function exec(command: string, options: ExecOptions): ExecOutputReturnValue | child.ChildProcess;

export interface ExecOutputReturnValue
{
    code: number;
    output: string;
}

When importing and using the library like this (which functions correctly in normal ES6 JavaScript)

import * as $ from 'shelljs';
const code =  $.exec(command, { silent: true }).code;

The Typescript compiler throws

error TS2339: Property 'code' does not exist on type 'ChildProcess | ExecOutputReturnValue'
.

Is there a way to safely access .code?

Answer №1

Having a union type means that all common members will be visible in its raw form.

If you wish to access more specific members, you must implement a type guard. By using a type guard, you will be able to utilize all the specific members of that type.

Below is a simplified example:

declare class Example {
    test(): string | number;
}

var obj = new Example();

var result = obj.test();

if (typeof result === 'string') {
    // Inside this block, result contains all properties of a string type
    result.
} else {
    // In this block, result contains all properties of a number type
    result.
}

In scenarios where applying a typeof check isn't feasible for certain types, you can explicitly inform the compiler that you are knowledgeable about what you're doing:

const data =  (<ExecOutputReturnValue >$.exec(command, { silent: true })).code;

Answer №2

Although this question may be dated, it can still provide valuable insights for anyone who stumbles upon it. In the latest TypeScript versions, utilizing user-defined type guards with type predicates offers a safer approach compared to simply assuming you know how to handle types correctly, potentially avoiding runtime errors in situations where assumptions lead to mistakes.

Let's consider the example provided by the original poster:

import * as $ from 'shelljs';

// defining our custom type guard, pay attention to the return type
const isExecOutput = (result: ExecOutputReturnValue | child.ChildProcess): result is ExecOutputReturnValue => {
    return (result as ExecOutputReturnValue).code !== undefined;
}

const result: ExecOutputReturnValue | child.ChildProcess = $.exec(command, { silent: true });

if (isExecOutput(result)) {
    doSomething(result.code); // safely accessing code property!
} else {
    // ... handling the other type gracefully
}

The crucial aspect of this implementation lies in the return type of the isExecOutput function: result is ExecOutputReturnValue. This concept known as a type predicate enables the creation of such user-defined type guards. For more detailed information, please refer to the official documentation available here.

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

Having trouble assigning a value of `undefined` to a TextField state in React hook

I am in need of setting the initial state for a TextField date to be undefined until the user makes a selection, and then allowing the user an easy way to reset the date back to undefined. In the code snippet below, the Reset button effectively resets par ...

Steps for utilizing response data as parameters for useInfiniteQueryHere is how you can make

On the specific page where I am implementing useInfiniteQuery const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, status } = useInfiniteQuery( ['posts', searchState], ({ pageParam = 1 }) => post.search({ . ...

Retrieving Vue data from parent components in a nested getter/setter context

<template> <div id="app"> {{ foo.bar }} <button @click="meaning++">click</button> <!--not reactive--> <button @click="foo.bar++">click2</button> </div> </templ ...

Intellisense in VSCode is failing to suggest subfolder exports

In my setup, I have a repository/module specifically designed to export TypeScript types into another project. Both of these projects are using TypeScript with ECMAScript modules. The relevant part of the tsconfig.json configuration is as follows: "ta ...

Is there a way to create a tuple property that can be called like a

I have a specific function in my code: function test(cb: Function | number) { let item = { height: 0} if(typeof cb === 'number') { item.height = cb; } if(typeof cb === 'object') { item.height = cb(); } } This function ...

Why does Typescript not enforce a specific return type for my function?

In my custom Factory function, I need to return a specific type: type Factory<T> = () => T; interface Widget { creationTime: number; } const createWidget: Factory<Widget> = () => { return { creationTime: Date.now(), foo: &a ...

An unusual error occurred stating that the `forEach` property does not exist on the given type

I am working on a chess game and encountering some Typescript errors that I'm struggling to comprehend. The issue arises in the following class method: clickEvent (e: MouseEvent): void { const coordinates: ClientRect = this.chessBoard.getBounding ...

Unable to resolve the Typescript module within a different file

I am in the process of transitioning my React app to TypeScript. Currently, everything is working fine. However, I encountered an issue after adding the TypeScript compiler and renaming files to .ts and .tsx extensions - it is now throwing a "module not fo ...

Aurelia: The passing down of views and view-models

In the process of developing an Aurelia app, I am tasked with creating functionality that allows users to display various lists for different resources. These lists share common features such as a toolbar with search and refresh capabilities, along with a ...

Adjusting canvas height in Storybook - Component does not fit properly due to low canvas height

I had a component that I needed to add to Storybook. It was working fine, but the styling was slightly off. I managed to resolve this by adding inline styling with position: absolute. Here is how it looks now: const Template: any = (args: any): any => ( ...

Exploring the potential of lazy loading with AngularJS 2 RC5 ngModule

I am currently utilizing the RC5 ngModule in my Angular project. In my app.module.ts file, the import statements and module setup are as follows: import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/plat ...

The cloud function that is callable is currently inactive and encountering errors upon invocation

I am experiencing issues with my Cloud Function which is supposed to call a request to the URL passed to it. This is my first time using TypeScript to make a request, so I added some print calls to troubleshoot the problem. However, the first log never app ...

Encountering an Invalid JSON error on the Developer console

I'm in the process of building a React application and aiming to establish a connection with my Back4App database. Within the Back4App dashboard, there exists a Person class containing data that needs to be retrieved. It appears that the call is being ...

When trying to pull a component from Svelte, I receive an error message stating "Selection Range

I'm still relatively new to svelte, so I might be handling things incorrectly. Whenever I attempt to separate my button component, regardless of whether I name the component ./Button.svelte, ./Button, Button.svelte, or try variations with capitalizat ...

Angular2's hidden feature isn't functioning properly

There is a common suggestion to use *ngIf better than [hidden] but in my scenario, I want to keep the element in the DOM without it being visible. In my component.html file, I have: <article #articleId id="bodyArticle" [hidden]="isClicked"></art ...

Disabling `no-dupe-keys` in ESLint does not seem to be effective

Currently, I am working on a project where I have incorporated Typescript and ESLint. However, I have encountered an issue with the error message stating: An object literal cannot have multiple properties with the same name. I am looking to disable this s ...

Tips for creating an input box that only accepts floating point numbers:

I have a custom component - a text box that I am using in two different places. In one location, it accepts integers and in another, floats. For the integer validation (where dataType=2), I have successfully implemented it. However, for the float validat ...

Using keyof on an indexed property within a generic type in Typescript does not effectively limit the available options

Imagine having an interface structure like this: export interface IHasIO { inputs: { [key: string]: string }, outputs: { [key: string]: string } } The goal is to develop a function that adheres to this interface as a generic type and ensur ...

Develop a Nativescript Angular component dynamically

Is there a way for me to dynamically generate a Component and retrieve a View object to insert into a StackLayout? ...

Exporting Typescript to Javascript files

I have created a sample TypeScript object with the following code: declare const S3 = "https://s3.amazonaws.com/xxx/icons"; declare const SVG = "svg-file-icons"; declare interface MyIcons { "image/jpeg": string; "image/jpg": string; } export const F ...