Specifying the data type for a "promisifier" function in TypeScript or Flow

I have developed a function that effectively converts a callback-style function in Node.js to a promise-style function.

export const promisify 
    : PromisifyT 
    = ( fn, ...args ) => {
        return new Promise( (resolve, reject) => {
            fn( ...args , (err, ...result) => {
                if (err) return reject(err)
                resolve(result)
            })
        }) as any
    }

Is there a way to declare PromisifyT in a manner that maps the input types of fn correctly to the newly generated function? Given that variadic type parameters are not yet supported, is there a limitation to the number of parameters that can be defined, maybe up to 2 or 3? Can it even be achieved?

Further, I encountered some challenges such as bad overload candidate matching during my initial attempts. Any advice?

Answer №1

It may be possible, but it's certainly not enjoyable.

This is the kind of UNPLEASANTNESS to which I am referring:

const resolveWith = (resolve, reject) => (err, data) =>
    err ? reject(err) : resolve(data);

type Resolver = (err:any, data:any) => void;

type Nodelike1<A> = (x:A, resolver:Resolver)=>void;
type Nodelike2<A,B> = (x:A, y:B, resolver:Resolver)=>void;
type Nodelike3<A,B,C> = (x:A, y:B, z:C, resolver:Resolver)=>void;

function promisify <A>(fn:Nodelike1<A>, x:A):Promise<any>;
function promisify <A, B>(fn:Nodelike2<A,B>, x: A, y:B):Promise<any>;
function promisify <A, B, C>(fn:Nodelike3<A,B,C>, x:A, y:B, z:C):Promise<any>;
function promisify (fn, x, y?, z?) {
  if (z != null) {
    return new Promise((resolve, reject) =>
      fn(x, y, z, resolveWith(resolve, reject)));
  } else if (y != null) {
    return new Promise((resolve, reject) =>
      fn(x, y, resolveWith(resolve, reject)));
  } else {
    return new Promise((resolve, reject) =>
      fn(x, resolveWith(resolve, reject)));
  }
}

const readFile = promisify((url:string, cb) => cb(null, "Bob"), url);

If the url passed in there is a string, it will work without a hitch. If you pass in a number, TypeScript will ask if you meant something else, because from the signature of the function it was passed, it wants a string.

As you can see, you can extend that off into the sunset, and turn it into a happy 300 line long bag full of fun with generics and going backwards through the arguments list. As a person who prefers functional programming, you can't imagine how dirty this makes me feel when I implement a compose or a curry... They are ghastly... the thought that the types are making my code safer in this case is only true if my code was written like this in the first place, rather than appeasing the compiler.

Ideally, I would take this one step further and make promisify return a function that expected the arguments x y and z separately from the function being passed in, so that it only looks that awkward once in the system.

But that's misery for another day.

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

What is the list of NPM packages already included in the AWS Lambda execution environment?

I was surprised to discover that the aws-sdk NPM module comes preinstalled in AWS Lambda using nodejs8.10. I couldn't find any information online about this. Are there other node.js modules that are also pre-installed in AWS Lambda? ...

Caution: The current version of the package has been deprecated and is no longer supported. It is advisable to update to the newest version available

While executing the npm install -g node-inspector command, I encountered an error message that prompts me to upgrade something. Unfortunately, I am unsure of what needs upgrading and how to do it. The complete error message I received is as follows: C:&b ...

The 'payload' property is not found within the 'Actions' type

I recently started using TypeScript and Visual Studio Code. I encountered the following issue: *[ts] Property 'payload' does not exist on type 'Actions'. This is my code: action.ts file: import { Action } from '@ngrx/store&apos ...

What is the method to incorporate type hints for my unique global Vue directives?

When I import or create a directive in an SFC file, I can receive TypeScript hints like this: import vFocus from 'my-custom-component-library'; View demo with ts hints However, if I globally register it in main.ts, the type hints are not availa ...

Why is my update with upsert: true not working in Express and Mongoose?

var logs = [{ mobilenumber: '1', ref: 3, points: 1000, ctype: 'mycredit', entry: 'sdfsdf', entry: 0 }, { mobilenumber: '1', ref: 6, points: 2000, ctype: 'mycredit', ...

The anchor tag is not being used in the function call in an Angular application

Having trouble with calling the logout function inside the login controller. The setup involves a simple login and logout system using ui-router. After successfully logging in and navigating to another page, I encountered issues with calling the logout fu ...

AngularJS form submission with and without page refresh

Looking to implement an AngularJS form directive where, if on /home page, redirect to /search/term and if already on /search page, submit without refreshing the page by simply changing the location. I am able to do both separately, but struggling to writ ...

I am experiencing difficulty with VS Code IntelliSense as it is not displaying certain classes for auto-import in my TypeScript project

I'm currently working on a project that has an entrypoint index.ts in the main folder, with all other files located in src (which are then built in dist). However, I've noticed that when I try to use autocomplete or quick fix to import existing ...

Use JavaScript to identify and color the intersecting area of two triangles that overlap on a webpage

I created two triangular divs using HTML and I am utilizing jQuery UI to enable them to be draggable. Now, my goal is to overlap these two triangles and change the color of the overlapping area to a different shade. Here is an example: https://i.sstatic. ...

Cloud function for Firestore to recursively update a subcollection or collection group

I've been working on this cloud function: import pLimit from "p-limit"; const syncNotificationsAvatar = async ( userId: string, change: Change<DocumentSnapshot> ) => { if (!change.before.get("published") || !change.after.exists) { ...

Explore innovative ways to display mathematical equations dynamically on the browser using NextJS version 13.3.0 or consider alternative solutions for showcasing math content with

I'm currently working on developing a web application that allows users to input LaTeX expressions, which are then dynamically displayed in another div or updated within the same input field. After some experimentation with Mathquill and NextJS v13.3 ...

Guide to retrieving objects in React.js

Struggling to extract a country from an online JSON file that I am currently fetching. I am attempting to streamline the process by creating a function to retrieve the country from the dataset and avoid repeating code. However, I am encountering difficulti ...

Using Karma-Jasmine to Import Spy without anyImplicitAny

If I include the configuration setting noImplicitAny in the tsconfig.json file of my Angular 4+ project: "noImplicitAny": true, ...and then try to import and use Spy in a unit test: import { Spy } from "karma-jasmine"; I encounter this console error wh ...

Challenges Encountered with Random Image Generator in JavaScript

I am encountering an issue with a script that is supposed to change the default images to a random image each time the page is refreshed. However, I keep receiving an error stating that boxID is null. Why is boxID null? <script type="text/javascript" ...

Error: The configuration property is not defined, causing a TypeError at Class.run ~/node_modules/angular-cli/tasks/serve.js on line 22

I'm encountering a persistent error on my production server that indicates a missing angular.json file, even though the file is present in the root of my project! Every time I run npm start, npm build, or npm test, I receive the same error message. ...

In Angular JS pagination, the previous filter value is stored in $LocalStorage for future reference

One view displays all order records in a tabular format with 10 records per page. A filter is set to show only paid orders, which pops up filtered data when selected. An issue arises when closing the pop-up window and navigating to the next page of the t ...

Configuration object for Webpack is not valid

I encountered an error while using webpack that says: Invalid configuration object. Webpack has been initialized with a configuration object that does not conform to the API schema. - configuration.resolve has an unknown property 'extension&ap ...

Position the circles in a way that they align along the circumference of a larger

I need help arranging a group of circles around another circle without any of them overlapping. I have all the radius measurements for each circle, as well as the coordinates for the target circle. The target circle will always be large enough to contain ...

After updating my Angular version from 8 to 9, an error has been thrown stating "It is not possible to assign the value 'undefined' to the template variable 'limit'"

Recently, I made updates to my Angular 8 project by switching it to the newest version of Angular 9. In one of the template's div elements, I declared a variable and everything seemed to be functioning correctly without any errors. To avoid initializi ...

Modify the paragraph's class upon clicking the radio button

I have been struggling with changing the color of <p> based on the radio button selected using two classes, red and blue. However, for some reason, it's not working as expected. If anyone has any insights or suggestions on how to fix this issue ...