TypeScript's Awaitable concept

Lately, I have been utilizing async / await functions quite frequently in my JavaScript projects. As I make the transition to TypeScript, I am gradually converting some sections of my code bases.

In certain scenarios, my functions require a function as an input that will be called and awaited. This function may return either a promise or a synchronous value. To handle this, I have created a custom type called Awaitable.

type Awaitable<T> = T | Promise<T>;

async function increment(getNumber: () => Awaitable<number>): Promise<number> {
  const num = await getNumber();
  return num + 1;
}

You can call this function like so:

// logs 43
increment(() => 42).then(result => {console.log(result)})

// logs 43
increment(() => Promise.resolve(42)).then(result => {console.log(result)})

While this approach works, it becomes cumbersome to specify Awaitable every time I work on projects involving async/await and TypeScript.

I find it hard to believe that TypeScript does not already have a built-in awaitable type. Could there be one that I am overlooking? Does TypeScript offer a native awaitable type?

Answer №1

It is my belief that the answer to this query is negative; there is no inherent type available for such a scenario.

Within lib.es5.d.ts and lib.es2015.promise.d.ts, the usage of T | PromiseLike<T> is evident in contexts where your Awaitable<T> would hold relevance. For example:

/**
 * Denotes the culmination of an asynchronous task
 */
interface Promise<T> {
    /**
     * Appends handlers for the resolution or rejection of the Promise.
     * @param onfulfilled The handler triggered upon Promise resolution.
     * @param onrejected The handler triggered upon Promise rejection.
     * @returns A Promise representing the outcome of either handler's execution.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    /**
     * Appends a handler specifically for Promise rejection.
     * @param onrejected The handler executed upon Promise rejection.
     * @returns A Promise representing the completion of the handler.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

In lib.es5.d.ts, the concept of Awaitable akin to what you described seems absent as they have detailed definitions for PromiseLike and Promise.

If such a feature was present, it is plausible to assume it would have been employed in said descriptions.

Note: Considering these clarifications, employing PromiseLike rather than Promise may be advisable when structuring your Awaitable:

type Awaitable<T> = T | PromiseLike<T>;

Answer №2

  1. async/await transforms statements into promises, ensuring that functions always return a promise.
  2. Anything can be awaited, whether async or not, making a custom Awaitable type potentially unnecessary.
async function test() {
    const foo = await 5;
    console.log(foo);

    const bar = await 'Hello World';
    console.log(bar);

    const foobar = await Promise.resolve('really async');
    console.log(foobar);
}

test();

Visit the TypeScript playground

Extra typing may not be needed, as functions will inherently include:

async function foo<T>(task: () => T | Promise<T>): Promise<T> {
  const result = await task();

  return result;
}

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

Error encountered when trying to update tree structure in TypeScript with new data due to incorrect array length validation

I have encountered an issue with my tree data structure in TypeScript. After running the updateInputArray(chatTree); function, I am getting an “invalid array length” error at the line totalArray.push(iteratorNode.data);. Furthermore, the browser freeze ...

Matching TypeScript against the resulting type of a string literal template

My type declaration looks like this: type To_String<N extends number> = `${N}` I have created a Type that maps the resulting string number as follows: type Remap<Number> = Number extends '0' ? 'is zero' : Number ...

Issue connecting to Oracle database: Unable to access properties of undefined (attempting to read '_getConnection')

Encountering an issue with node oracle connection. It was successfully connected before in the same application, but now it is not working after updating the node version. The connection string seems fine as the same connection is working in another appli ...

Exploring the process of retrieving data from localStorage in Next.js 13

Having recently delved into the realm of Next JS, I've encountered a hurdle when it comes to creating middleware within Next. My aim is to retrieve data from local storage, but I keep hitting roadblocks. middleware.ts import { key, timeEncryptKey, to ...

What is the process for displaying the attributes of a custom object in Typescript?

I need help returning an array of prop: value pairs for a custom object using the myObject[stringProp] syntax. However, I keep encountering this error message: TS7053: Element implicitly has an 'any' type because expression of type 'str ...

Angular 13: How to Handle an Empty FormData Object When Uploading Multiple Images

I attempted to upload multiple images using "angular 13", but I'm unable to retrieve the uploaded file in the payload. The formData appears empty in the console. Any suggestions on how to resolve this issue? Here is the HTML code: <form [formGro ...

Angular's HostListener triggers twice when the browser's back button is clicked

In my Angular application, I have implemented a @HostListener that triggers when the back button on the browser is clicked. However, I have noticed that the event handler seems to be firing twice - prompting two dialogue boxes when clicking 'Ok'. ...

Tips for building a diverse array of data types and effectively utilizing them based on their specific type in Typescript

Trying to store both custom types, Graphic and Asset, in the same array is proving to be a challenge. The goal is to access them and retain their individual type information. const trail: Array<Graphic | Asset> = []; for (let index = 0; index < t ...

What is the reason for TestBed using waitForAsync in beforeEach instead of directly using async/await to compileComponents?

UPDATE: I submitted a problem report to Angular and they have since made changes to the documentation: https://github.com/angular/angular/issues/39740 When working on Angular tests, it is common practice to set up the beforeEach method like this: befo ...

Adjusting ngClass dynamically as values change

Currently, I am facing a situation where I want to dynamically add a class to my view using ngClass based on changes in the output value. The output value is dependent on the response received from an API, and I am fetching data from the endpoint every sec ...

What is the best way to assign the selected attribute to my mat-option element?

I am encountering an issue with my mat-select and mat-option control. I am trying to apply the selected attribute to the first mat-option control without using binding on [(ngModel)] or [(value)]. The mat-option control is being generated by the *ngFor d ...

Discover the steps to initiate Firefox in Incognito Mode using NodeJS and Selenium!

Can anyone help me figure out how to launch Firefox in private mode using TypeScript? I have attempted the following code but it doesn't seem to work: static async LaunchFirefoxInPrivateMode():Promise<WebDriver> { //Setting up firefox capab ...

What is the solution to resolving a Typescript error within an imported library?

I've encountered a Typescript error that I'm unable to resolve because it doesn't originate from my code. The issue arises when I import a NextJs/React module named next-seo and use it as instructed: <NextSeo config={seoConfig} /> The ...

Can the inclusion of additional parameters compromise the type safety in TypeScript?

For demonstration purposes, let's consider this example: (playground) type F0 = (x?: string) => void type F1 = () => void type F2 = (x: number) => void const f0: F0 = (x) => console.log(x, typeof(x)) const f1: F1 = f0 const f2: F2 = f1 f ...

Steps for making a GET request from an API in Angular 7

Currently, I am attempting to retrieve JSON data using HttpClient in Angular 7. The code is functioning properly, but I am exploring the option of fetching the data directly from the API URL instead of relying on the const IMAGES array. import { Injectable ...

The attribute is not found on the combined type

After combing through various questions on stackoverflow, I couldn't find a solution to my specific case. This is the scenario: interface FruitBox { name: string desc: { 'orange': number; 'banana': number; } } interf ...

Angular definitely typed does not convert into JavaScript

After installing TypeScript on my VS2013, I obtained the Angular 1.5 Definitely Typed from the NuGet package manager. Although angular.d.ts and its components do not generate angular.js file, when I create another TypeScript file like file1.ts, the file1. ...

Ways to implement useState in a TypeScript environment

Hello, I am currently using TypeScript in React and trying to utilize the useState hook, but encountering an error. Here is my code: import React , { useState } from "react"; interface UserData { name: string; } export default function AtbComponent( ...

Determining function return property type in Typescript by mapping interface argument property

In order to manage messaging between the browser and a web worker, I have developed a generic class. Each side creates a class that can send specific messages and acknowledge them on the other side with a returned result in a payload. The implementation is ...

What is the best way to create a custom type guard for a type discriminator in TypeScript?

Suppose there are objects with a property called _type_ used to store runtime type information. interface Foo { _type_: '<foo>'; thing1: string; } interface Bar { _type_: '<bar>' thing2: number; } function helpme(i ...