Decoding intricate type declarations in Typescript

Exploring the intricacies of this type declaration:

export interface Thenable<R> {
    then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>
    then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>
    catch<U>(onRejected?: (error: any) => U | Thenable<U>): Thenable<U>
}

I have a good grasp of its function. For instance, I can utilize it like so:

type MessagesSendResponse = ...
export const announce = (options: sendParameters):Thenable<MessagesSendResponse> => {
 return Promise.all([emailMessage(options),smsMessage(options)] 
}

This setup allows for distinction between contexts:

const val1 = announce(..)

where val1 is recognized as a Thenable/Promise, while in this scenario

const val2 = await announce(..)

val2 is identified as type MessagesSendResponse

The aspect that puzzles me regarding the Thenable interface is:

  1. What does it signify when stating Thenable<U>? I comprehend that U represents a generic type, but what is conveyed by Thenable<U>? Is there an alternate way to express this?

  2. In some way, this definition specifies that a function yields a thenable/promise which subsequently returns a generic value. However, both the interface type and the return value for both then and catch are labeled as Thenable<U>. It seems to indicate that it returns itself, considering a thenable can produce another thenable, but how does it recognize that the outcome is MessagesSemdResponse if it declares to yield Thenable<U>? Is there a built-in feature in the IDE aiding with this?

This query stems from my confusion about question 2. Any suggestions or references related to this pattern would be greatly appreciated, as I could not locate similar information in my research.

Answer №1

Having a Thenable<T> indicates possession of an object that can yield a value of type T.
Alternatively, labeling it as Promise<T> provides access to a promise pertaining to a value of type T.

Given that promises are typically used for asynchronous tasks, the provision is not a direct "value return" but rather furnishes an interface for obtaining a reference to said value upon availability.

The rationale behind then/catch yielding Thenable<T> is to enable method chaining:

announce(...).then(value => {
    ...
}).catch(error => {
    ...
});

The function passed into then/catch gets invoked when the value materializes or if any issues arise.

It's important to note that the promise produced by then/catch is distinct from the original one, as it generates a new instance where the generic type is inferred based on the return value of the provided function. For example:

const fn = (): Promise<string> => {
    return Promise.resolve("43"); 
}

fn().then(str => Number(str)).then(num => console.log(num))

The compiler distinguishes that str is a string and num is a number.

Further insights on this process can be found here: MDN article on promise chaining


Edit

In the signature:

when<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U> 

onFulfilled:
A function anticipating a value of type R, which must deliver either a value of type U or a Thenable referencing U.

onRejected:
A function expecting a value of type any, which should provide either a value of type U or a Thenable pointing to U.

returns:
Produces a fresh instance of Thenable for a value of type U, representing the product of executing either onFulfilled or onRejected.

Answer №2

section1:

export interface Thenable<R> {

We are creating a generic interface with a type parameter R.

section2:

then<U>(onFulfilled?: ...,  onRejected?: ...): ...

This includes a method called then, which accepts two parameters onFulfilled and onRejected. The method itself is generic, based on another type parameter U.

section3, the more intriguing part:

onFulfilled is defined as follows:

(value: R) => U | Thenable<U>

This indicates that onFulfilled can be either a function that takes R and returns U, or another Thenable<U>.

The relationship between R and U: when you have a Thenable<R>, its then method expects a callback that receives a value of type

R</code (produced by the original thenable) and should return <code>U
. Alternatively, it can accept another Thenable<U>.

The result of this then method, therefore, is a Thenable<U>.

In essence, this is how promise chaining is described using types.

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

`Running ng serve will result in the creation of a 'dist' folder within each app sub

Since beginning my project, I have encountered an issue that is both normal and frustrating. The dist folder is being created with incomplete information related to the components inside it. dashboard dist (unwanted) components panel dist (unwanted) c ...

Typically used to describe an item with a variable amount of string attributes

I have a TypeScript generic for an object with an unspecified number of string parameters like this: type Params<T extends string[]> = Record<T[number], string>; However, every time I want to use it, I need to define it with an array like so: ...

How can I access a child element within an element using Protractor?

I'm struggling to access the child element of an ng-repeat element. After searching for solutions, I came across suggestions that didn't work for me. One tip was to try something like this: var parent = element(by.repeater('')); var c ...

What is the method to prevent the Task running terminal from closing automatically once the program finishes in Visual Studio Code?

Currently, I am a beginner in learning c++ and utilizing visual studio code as my Integrated Development Environment (IDE). For instance, within a cpp file, there lies the hello world program. Through VSC, the cpp file is compiled utilizing a task titled B ...

Error detected in Ionic socket.io chat page

I'm encountering a minor issue with my chat page. I'm trying to create a chat application, and everything is working fine except for the chat button, which causes the app to crash. Being a beginner, I might be missing something obvious. The issue ...

The animation for the menuItem in the MenuBar in React is not functioning as expected

In my project, I am using React with TypeScript and implementing animations with framer-motion. One issue I am facing is that when the button is pressed to open the menubar, the menuItem should move according to the frame-motion animation but it is not. I ...

Error: Issue determining the type of variable. Unable to eliminate type 'any'

I am trying to load some widgets from a template object (possibly JSON in the future). Here's an example: type RectangleTemplate = { name: 'Rectangle'; props: { width: number; height: number; } }; type ButtonTemplate = { nam ...

Error message when using Angular material table: Unable to access properties of undefined (specifically 'headerCell')

Having an issue with the header checkbox while working with Angular using material-table. Whenever I try to add a 4th column for the checkbox, I encounter a strange error message: ERROR TypeError: Cannot read properties of undefined (reading 'header ...

Dynamic React Hooks Input

I have a form component that allows users to add an input when clicking a button, creating new teams in which players can be added. The issue is that when filling in a value, all inputs get filled with the same value because they are using the same state. ...

Using dynamic imports to enhance code by adding the `.at()` function can lead to errors in the process

Below is the content of my .tsconfig configuration file: { "compilerOptions": { "target": "es6", "baseUrl": "./src", "lib": ["dom", "dom.iterable", "esnext&q ...

Unable to configure unit tests for Vue project using Typescript due to TypeError: Unable to destructure property `polyfills` of 'undefined' or 'null'

I've been working on adding unit tests for an existing Vue project that uses Typescript. I followed the guidelines provided by vue-test-utils for using Typescript, but when I ran the test, I encountered an error message stating: TypeError: Cannot d ...

Prevent coverage tracking for files or paths enclosed in square brackets in jest

I am trying to exclude a specific file from test coverage in Jest by modifying the collectCoverageFrom array. The file name contains square brackets, and I have added an entry with a negation for this file. collectCoverageFrom: [ './src/**/*.{js ...

Knexfile Doesn't Utilize TypeScript Paths

When attempting to run a migration, I encountered an issue with Knex. My Knexfile is in Typescript and uses path aliases from tsconfig.json. However, Knex is throwing an error stating Cannot find module '@foo-alias/bar-module'. What adjustments d ...

Error: Unable to locate module: Unable to resolve '@/src/components/layout/Header'

Compilation Error Encountered issue in Next.js (version 14.2.4) while trying to locate '@/src/components/layout/Header' at ./src/app/layout.js:3:1 Suddenly, 2 days ago the code was functioning properly but now it's throwing this error. Er ...

Having trouble with JavaScript's Date.getUTCMilliSeconds() function?

I have a straightforward question for you. Take a look at this Angular App and try to create a new date, then print the number of UTC milliseconds of that date in the console. Can you figure out why it is returning zero? ...

Ways to resolve the issue of 'message' property lacking an initializer in TypeScript without suppressing errors

Currently, in the process of using TypeScript and Sequelize to create a model within Node.js. import { Table, Column, Model, AllowNull } from 'sequelize-typescript'; @Table class Person extends Model { @Column @AllowNull(false) name: strin ...

"Typescript modules: the definition of 'defined' has not been found

Can anyone shed some light on TypeScript 1.8 modules for me, please? So, I have a file named SomeClass.ts: export class SomeClass { } Now, in my app.ts, I'm importing SomeClass: import {SomeClass} from "./SomeClass"; var xxx = new SomeClass(); A ...

When working with multiple charts on Angular ChartJs, the data may not display properly

My goal is to display multiple Charts in a single page using Angular. I came across an Example that uses ViewChildren: const baseConfig: Chart.ChartConfiguration = { type: 'pie', options: { responsive: true, } }; @ViewChildren('c ...

Unraveling the mysteries of intricate JSON array and object data

I am currently facing a challenge in extracting complex JSON data for my Angular application. Here is an example of how the data is structured: { "members": [ { "member_id": "1", "first_name": "John", " ...

Is your Angular5 service failing to transmit data?

I have two components in my code, A and B. Component A contains a form with data that I want to send to component B. However, it seems like component B is not receiving any data. Here is the code for Component A: import { MyService } from 'path/my ...