Converting a JavaScript function to TypeScript with class-like variables inside: a step-by-step guide

During the process of converting a codebase to TypeScript, I encountered something unfamiliar. In particular, there are two functions with what appear to be class-like variables within them. The following function is one that caught my attention:

const wait = (ms) =>
    new Promise((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            delete wait.reject;
            resolve();
        }, ms);

        wait.reject = (reason) => {
            clearTimeout(timeoutId);
            reject(reason);
        };
    });

As you can observe, it contains a variable called wait.reject, which is an arrow function defined at the end. Despite deleting the reject variable after a certain time duration.

To incorporate typing for this scenario, I resorted to using (wait as { reject: ... }.reject, as shown below:

const wait = (ms: number) =>
    new Promise<void>((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            delete (wait as { reject?: () => void }).reject;
            resolve();
        }, ms);

        (wait as { reject?: (reason: string) => void }).reject = (reason: string) => {
            clearTimeout(timeoutId);
            reject(reason);
        };
    });

Naturally, relying on as is not the most favorable approach. Is there anyone who knows the correct way to type this? This issue extends beyond just one function in the codebase.

Your assistance and insights are greatly appreciated! :)

Answer №1

Let me begin by strongly suggesting against the current implementation of the wait function in your code. If you happen to make two consecutive calls to wait, the second call's reject will overwrite the first one. It might be more beneficial to return an object with separate promise and reject properties, enabling the consumer of the function to manage them independently. This approach is especially useful when refactoring existing codebases in TypeScript.

Now, let's discuss the types:

The wait function expects a numeric parameter and returns a promise that resolves to void. Additionally, it includes an optional reject property, which accepts any reason and returns void. By defining this type as a combination of a function and an object with the optional reject property, we can ensure better control over error handling:

type WaitFn = ((ms: number) => Promise<void>) & { reject?: (reason: any) => void };

To utilize this type within the wait function, follow this pattern:

const wait: WaitFn = (ms: number) =>
    new Promise<void>((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            delete wait.reject;
            resolve();
        }, ms);

        wait.reject = (reason) => {
            clearTimeout(timeoutId);
            reject(reason);
        };
    });

If desired, the type annotations for ms and new Promise can be omitted since they can be inferred from the defined WaitFn type:

const wait: WaitFn = (ms) =>
    new Promise((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            delete wait.reject;
            resolve();
        }, ms);

        wait.reject = (reason) => {
            clearTimeout(timeoutId);
            reject(reason);
        };
    });

In summary, I recommend using the revised approach where the wait function returns an object containing both the promise and reject elements separately, preventing any potential overlapping issues in case of multiple function calls.

This updated structure would look like this:

const wait = (ms: number) => {
    let promiseReject: (reason: any) => void;
    let timeoutId: ReturnType<typeof setTimeout>;
    const promise = new Promise<void>((resolve, _reject) => {
        promiseReject = _reject;
        timeoutId = setTimeout(() => {
            resolve();
        }, ms);
    });
    let reject = (reason: any) => {
        clearTimeout(timeoutId);
        promiseReject(reason);
    };
    return { promise, reject };
};

To use this function, simply assign the returned values to variables and handle them accordingly:

const { promise, reject } = wait(1000);
// Handle the promise or call reject if needed...

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

Tips for restricting access to specific routes in VueJS using Store state

Once a user is authorized, their user type is saved to the state. Based on this type, I need to dynamically show or hide specific routes. src/store/index.js: import Vue from "vue"; import Vuex from "vuex"; import getters from "./getters"; import user from ...

What is the best way to show the current date and time?

To filter out and display only the date from the array that is the largest compared to the current date, all dates are displayed. const states = States.select().exec() var curr = new Date(); for (var i = 0; i < states.length; i++) { var maxDate = ...

org.openqa.selenium.WebDriverException: unexpected issue: Chrome failed to initiate: crashed.(chrome inaccessible)

Having issues running Java script (selenium framework) on Chrome. Tried various solutions but still facing problems: Unchecked run as admin Added arguments Snippet of the code: ChromeOptions options = new ChromeOptions(); //options.setExperimentalOption ...

Retrieve the value of a property in a JavaScript object by specifying a dynamic key for the property

As I work on my current project, I find myself immersed in a world of SVG animations. The challenge lies in triggering these animations as the user scrolls down to view the SVGs. To address this, I took the approach of creating functions for each Snap.SVG ...

ReactJS Components failing to load on initial site visit, only appearing after refreshing for the second time

var img; var dateFormat = require('dateformat'); var count; let arrayIMG = [] var storage = firebase.storage(); var storeRef = storage.ref('images/') const config = { ... }; if (!firebase.apps. ...

Unlocking the potential of deeply nested child objects

I have a recursively typed object that I want to retrieve the keys and any child keys of a specific type from. For example, I am looking to extract a union type consisting of: '/another' | '/parent' | '/child' Here is an il ...

Vue.js: Issue with updating list in parent component when using child component

I'm encountering an issue when utilizing a child component, the list fails to update based on a prop that is passed into it. When there are changes in the comments array data, the list does not reflect those updates if it uses the child component < ...

I am interested in having the push notification feature function specifically for registered users

In an attempt to register the device for push notification using the PhoneGap plugin, I am encountering an issue. After the AJAX success action is called, the registration action does not alert the registration ID. Can someone help figure out what's g ...

Why am I not receiving any results from the communication between JavaScript and PHP using an HTTP GET request?

Currently, I have a small JavaScript program running within an HTML5 canvas and included a HTTP GET request function in my JavaScript code. The function itself is functioning properly as I tested it with multiple examples from the internet, all of which wo ...

The Tailwind CSS Chrome extension is causing disruptions on the websites I view

Currently, I am in the process of creating a chrome extension using various tools like React, Typescript, TailwindCSS, and a custom Webpack configuration. To enhance user experience, I have modified the default action in manifest.json so that clicking on t ...

Why is the Routes module failing to acknowledge the component?

I am in the process of developing my own Portfolio and decided to use Angular 12. Despite following all of the instructions on angular.io, I am facing challenges with Routing. To demonstrate my work more effectively, I have created a Stack Blitz: My Portf ...

AngularJS selection controls: checkbox and dropdown menus

Can someone provide an example that includes a dropdown menu and checkboxes? The options in the checkbox list should match the data in the dropdown. Once a value is selected from the dropdown, the corresponding checkbox option should be automatically chec ...

techniques for accessing HTML source code through an AJAX call

I am trying to retrieve the HTML source of a specific URL using an AJAX call, Here is what I have so far: url: "http://google.com", type: "GET", dataType: "jsonp", context: document.doctype }).done(function ...

Encountered Error: Rendered an excessive number of hooks beyond the previous render in the framework of Typescript and

I am currently working on integrating Typescript and Context API in an application. Specifically, I am focusing on setting up the Context API for handling login functionality. However, I encountered the following error message: Error: Rendered more hooks ...

When the query result is received in Angular TypeScript, translate epoch time into a time string

Here is the dilemma I am currently facing: I have an Angular script that requests data from a backend service and receives query results to display to the user. One of the fields in the query response is a time stamp, which is currently in epoch time forma ...

Angular 2 ngFor generates a collection of rows and columns forming a single large column

It seems that ngfor is generating divs one by one, resulting in a poor design where they are stacked on top of each other. I would like to achieve a layout like this: [1] [2] [3] [4] [5] [6] However, the current outcome looks like this: [ 1 ] [ 2 ] [ 3 ...

What causes React JS to continuously render in an infinite loop when using hooks and useState

I am struggling with updating the current state of my component based on a result using a custom hook in React. Whenever I try to update it, I end up in an infinite loop rendering due to my usage of the useState() hook. I am still new to working with Rea ...

Changing the website address | Node.js | Express

Is there a way to redirect incoming requests to different endpoints depending on a query parameter in the request? For instance, http://localhost:9000/?category=xyz&param1=...&param2=... The category value can be any of [abc, ijk, pqr, xyz]. Gi ...

Exploring (nested) data structures in JavaScript

I'm struggling to understand if my issue lies in how I am organizing my array or in how I am accessing it. The idea is simple: I want to have an array of car makes and models that I can access for display purposes. const carBrands = [ { Audi: { ...

What are the steps to redirect from a nested route back to the top route using Node.js with Express?

Is there a way to redirect from a nested route to a top route in Express.js? In the following code snippet, how can we make the callback for the route /toproute/nested redirect to /profile instead of /toproute/profile? // app.js const express = require(& ...