Determine the data type of a function's parameter

Given that TypeScript is a superset of Javascript and 'Type' is not present in the resulting js files, does this mean manipulating 'type' will not function as intended? Furthermore, are there any alternative approaches to achieve this without using two separate methods?

type VoidToVoidFunc = () => void;
type VoidToPromiseVoidFunc = () => Promise<void>;

async function add(func: VoidToVoidFunc  | VoidToPromiseVoidFunc) {
    if (typeof func == typeof VoidToVoidFunc)
        func(); 
    else
        await func();
}

add(() => console.log(1));

Answer №1

Looking at it here, you'll notice that when a value that is not thenable comes after the await keyword, a fulfilled Promise is created and utilized.

Therefore, the easiest way to implement this is by using await with the function call result. Essentially, if the value is not already a Promise, await will wrap it in one:

type FunctionReturnsVoid = () => void;
type FunctionReturnsPromiseVoid = () => Promise<void>;

async function performAction(func: FunctionReturnsVoid  | FunctionReturnsPromiseVoid) {
  await func();
  // Carry out additional tasks
}

performAction(() => console.log(1));

This behavior is accurately typed by TypeScript; indeed, if you hover over the variable after writing const output = await func();, its type will be correctly inferred as void.

I suppose the internal structure of the type returned by await might resemble something like this:

type Await<T> = T extends Promise<infer R> ? R : T

Try it out on the TypeScript playground

Answer №2

The most efficient and easy-to-understand solution is the simplest approach

type VoidToVoidFunc = () => void;
type VoidToPromiseVoidFunc = () => Promise<void>;

async function execute(func: VoidToVoidFunc | VoidToPromiseVoidFunc) {
  await func();
}

execute(() => console.log("sync function", 1));

execute(async () => {
  await delay(1000);
  console.log("async function", 2);
});


function delay(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Playground Link

async function execute(func) {
  await func();
}

execute(() => console.log("sync function", 1));

execute(async () => {
  await delay(1000);
  console.log("async function", 2);
});

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

As demonstrated above, utilizing the await keyword on a non-Promise value essentially wraps and unwraps it. Since your execute function is marked as async, it will always return a Promise and execute asynchronously (scheduled for the next tick), making complex solutions unnecessary in this scenario.

Refer to the async function and Promise documentation on MDN for further information.

Hence, while there are alternative methods to determine if a callback may or may not return a Promise, such as

type VoidToVoidFunc = () => void;
type VoidToPromiseVoidFunc = () => Promise<void>;

async function execute(func: VoidToVoidFunc | VoidToPromiseVoidFunc) {
  const possiblePromise = func();
  if (possiblePromise && 'then' in possiblePromise) {
    await possiblePromise;
  }
}

execute(() => console.log("sync function", 1));

execute(async () => {
  await delay(1000);
  console.log("async function", 2);
});


function delay(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Playground Link

async function execute(func) {
  const possiblePromise = func();
  if (possiblePromise && 'then' in possiblePromise) {
    await possiblePromise;
  }
}

execute(() => console.log("sync function", 1));

execute(async () => {
  await delay(1000);
  console.log("async function", 2);
});

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Simplicity is key in this situation.

Note: The void type in TypeScript defines the function contract and guides type inference. However, every JavaScript and TypeScript function actually returns a value. In this case, the func parameter in execute can return either undefined or a Promise that resolves to undefined.

Answer №3

await simply requires its operand to be a promise! It will return the awaited expression's value only if it is not a promise.

await expression

expression
This can be a Promise, a thenable object, or any other value for which you want to wait.

Reference: MDN

Even a synchronous subroutine is acceptable. Therefore, you can simplify your code like this:

type VoidToVoidFunc = () => void;
type VoidToPromiseVoidFunc = () => Promise;

async function execute(func: VoidToVoidFunc  | VoidToPromiseVoidFunc) {
    await func(); /* <—- works whether <b>func()</b> is sync or async. */
}

execute(() => console.log(1));

Answer №4

It's true that you can't rely on the type once compiled to JavaScript.

Instead, you can examine the return value of your function to determine if you need to await the promise or resolve the value immediately.

type VoidToVoidFunc = () => void;
type VoidToPromiseVoidFunc = () => Promise<void>;

async function add(func: VoidToVoidFunc  | VoidToPromiseVoidFunc) {
    /* either a pending promise or undefined */
    const value = func();

    if(value instanceof Promise) {
      await value;
    }
}

If the function returns a promise, you will await it. If not, you will skip the awaiting part.

The demonstration below is in plain JavaScript to illustrate how this concept works:

async function add(func) {
    /* either a pending promise or undefined */
    const value = func();

    if(value instanceof Promise) {
      await value;
    }
}

const asyncFunction = async () => {
  return new Promise(resolve => {
    setTimeout(() => {
        console.log('resolving promised function');
        resolve();
    }, 1000)
  });
};

const syncFunction = () => {
    console.log('resolving normal function')
}

add(asyncFunction);

add(syncFunction);

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

Using Node.js to instantly redirect users to a new page while also sending them important

I am currently working on building a basic server for HTML pages using Node.js. The issue I am facing is that when I go to (for instance) http://localhost:8080/someDirectory, the browser mistakenly treats someDirectory as a file (when in reality, it is int ...

The combination of jQuery, using .load method in javascript to prevent scrolling up, making XMLHttpRequest requests, updating .innerHTML elements, and troubleshooting CSS/JS

While utilizing this code, CSS and Javascript are disabled (only HTML loads): function loadContent(limit) { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (xhttp.readyState == 4 && xhttp.status ...

Issue with Date.js: Date.monday() function is not functioning as expected

I am attempting to utilize Datejs for some date functions. However, I am encountering issues with functions like Date.march() and Date.monday(). I have downloaded the necessary files from the Datejs website. Upon inspecting with firebug, it appears that my ...

Showing Arrays in Angular on HTML Page

I have created an array that stores multiple arrays with 3 indexes each. An example of the structure looks like this: (3) [Array(3), Array(3), Array(3)] 0: (3) [199.4, 10.5, 19] 1: (3) [47.2, 2.1, 23] 2: (3) [133.6, 5.3, 25] In my HTML, I want to display ...

Using VueJS to transfer data from the main element to child components via router-view

Typically, when I need a variable that multiple child components should access, I usually store it in the data object of my root Vue element and then pass it down to child components through properties. However, since I've started using vue-router, m ...

Utilizing Java's IE/Chrome HTML-Renderer or managing file storage with JavaScript

I am currently in the process of developing a small application that operates offline only. I want to keep it simple and was considering using HTML, CSS, and JavaScript as they should suffice for my needs. However, I require access to the filesystem to sto ...

The UI-SELECT feature in angular-js does not seem to be displaying

I've added the necessary ui-select js and css files to my page : <link href="assets/plugins/ui-select/select.min.css" rel="stylesheet" type="text/css" <script src="assets/plugins/ui-select/select.min.js" tyle="text/javascript" </script> ...

Exploring the Intersection of VueJs Life Cycle Methods and JavaScript

I am currently facing an issue while attempting to create a carousel using Siema. The problem arises when the slides are generated using a v-for loop. Although I am not receiving any errors, I suspect that the new Siema function is being called before the ...

Issues with jQuery autocomplete functionality on certain elements are not uncommon

I've been experimenting with creating a user script for Opera using Greasemonkey to implement autocomplete functionality on input elements within web pages. However, I've encountered some issues with the script not working as expected. Initially ...

Using Javascript to validate a PHP form that's dynamically generated: A comprehensive guide

I have designed a PHP form that requires users to select a radio button before submitting the form. Here is how it is set up: <form name="films" action="showing.php" method="post"> <table id="filmtable"> <tr><th>Title</th>< ...

Conceal form after submission - Django 1.6

I'm currently working on a Django 1.6 project where I have this form: <form action="/proyecto/" method="POST" id="myform"> {% csrf_token %} <table> <span class="Separador_Modulo">& ...

Modify the text tone within a specific cell

Welcome to my unique webpage where I have implemented a special feature. The text highlighted in red represents the unique identifier for each individual cell. Interestingly, below there is an input field containing the code "0099CC", which corresponds to ...

Load data from a JSON flat file and dynamically populate new <li> elements with it

I'm attempting to utilize data from a json flat file in order to: Create new list items Fill specific classes within the newly created list items The json data appears as follows: { "event": { "title": "Title of event", "preface": "Prefa ...

Is there a way to stop Babel from transpiling JavaScript and wrapping its output in a define([], function(){}) block?

I am currently working on transpiling ES6 files to ES5 using Babel in order to be compatible with RequireJS (Magento 2). However, I have encountered an issue that has me stuck. Here is the Babel configuration: const presets = [ [ "@babel/ ...

The Angular2 Renderer successfully renders the Svg rect element, but it is not displayed on the webpage

I am looking to generate a bar chart utilizing SVG with rectangles as the bars. Here is the relevant code: barchart-one.html <svg #svgone width="400" height="250" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 250"> <g #abcd>< ...

After the submit button is disabled, the form fails to be submitted

Hello, I am having an issue with disabling my button after the form is submitted. The button gets disabled, but the PHP code does not execute. I have tried various scripts from the internet, but they all seem to have the same result: the button gets disab ...

5% of the time, Ajax fails and the response is "error"

When utilizing jQuery for Ajax calls, I have encountered a situation where the call fails approximately 5% of the time. To troubleshoot and understand the issue better, I implement this code: $.ajax({ type:'POST', url:'somepage.php ...

JavaScript popup is no more visible on the webpage

Recently, I implemented a pop-up on my website that used cookies to prevent it from appearing every time a user visited a page. However, after making this change, the pop-up stopped showing altogether. Despite my best efforts in testing, researching, and s ...

Unable to properly call a JavaScript file from an HTML file

Executing this simple code with the JavaScript line placed under Script tags will trigger the desired alert message: <!DOCTYPE html> <!-- saved from url=(0014)about:internet --> <html> <body> <script type="text/javascript" > ...

How can jQuery determine the amount of line breaks within a div element?

My div wrap size is a percentage of the screen width, containing multiple .item divs that break into new lines as the window size decreases. I attempted to calculate the total number of boxes that could fit based on their widths compared to the container ...