Make sure that each function within a generic interface is asynchronous

Imagine having an abstract class that accepts a generic type

export abstract class RegisterableClass<InstanceType>

and a class that implements it like this:

class UserService extends RegisterableClass<IUserService> implements IUserService {
  someConstant: 42,
  async getAllUsers: (): User[] => {...}
}

defined by the following interface:

interface IUserService {
  someConstant: number;
  getAllUsers: () => Promise<User[]>;
}

The goal is to ensure that the generic type, IUserService, provided in this case, ensures that all methods are asynchronous.

Is there a way to achieve this statically with TypeScript?

Perhaps using

Extract<keyof IUserService, Function>
somehow? And then passing it to another construct?

In other words, if you try to extend RegisterableClass and pass a generic type where not all functions are async, TypeScript should not compile the code.

Answer №1

If I comprehend correctly, your objective is to restrict the type parameter passed to RegisterableClass to have methods that are all asynchronous, meaning they return promises.

If this is the case, you can achieve it like this:

type AllMethodsAreAsync<I> = {
    [K in keyof I]: I[K] extends (...args: any) => infer R ?
    R extends Promise<any> ? I[K] : (...args: any) => Promise<R> :
    I[K]
}

export abstract class RegisterableClass<I extends AllMethodsAreAsync<I>> {
    // ... something involving I, ideally
}

The type function AllMethodsAreAsync<I> will be the same as I if I is an object type with function-valued properties that return promises. However, if I contains function-valued properties that do not return promises, the corresponding property in AllMethodsAreAsync<I> will be modified to return a promise instead.

Then, if

I extends AllMethodsAreAsync<I>
meets the generic constraint, everything is fine. If not, you will receive an error indicating exactly which part of I is causing the issue. Like this:

// adding this so standalone example works
type User = { u: string }; 

interface IOkayService {
    someConstant: number;
    getAllUsers(): Promise<User[]>;
}

type OkayService = RegisterableClass<IOkayService>; // okay

interface IBadService {
    someConstant: number;
    getAllUsers(): Promise<User[]>;
    getSomethingSynchronously(x: string): number;
}

type BadService = RegisterableClass<IBadService>; // error!
//                                  ~~~~~~~~~~~
// Type 'IBadService' does not satisfy the constraint 'AllMethodsAreAsync<IBadService>'.
// Types of property 'getSomethingSynchronously' are incompatible.
// Type '(x: string) => number' is not assignable to type '(...args: any) => Promise<number>'.
// Type 'number' is not assignable to type 'Promise<number>'.

Playground link

Is this in alignment with your needs? I wish you the best of luck!

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 in Electron: Uncaught TypeError - Attempting to assign a value to an undefined property 'title'

Currently, I am in the process of creating some classes for my code using TypeScript, Material UI, React, and Electron. Everything seems to be running smoothly when tested on CodeSandbox. However, when I try to run the code in the Electron environment, all ...

Can metadata be attached to data models in Angular for annotation purposes?

Looking to add some metadata annotations to a simple data model export class Certification { title: string; certificationType?: CertificationType; validTo?: number; description?: string; externalIdentifier: Guid; constructor() { ...

A guide on implementing Redux Toolkit in an SPFX (typescript) project

I am currently working on developing SPFX components using typescript and webpack. My goal is to integrate redux toolkit into my project, but it is causing compilation errors. Here is what I have done so far: Created the project structure using yo Insta ...

What is the best way to handle success data in React Query?

Currently, I have an API call function (using httpClient as axios instance) interface IRegisterResponse { accessToken: string; } export const register = async ({ name, password, token, }: IRegisterParams) => await httpClient.post<IRegiste ...

An issue occurred while attempting to access the `/blog` page in a next.js project

The Challenge: As I work on developing a blog using Next.js and Sanity, I am facing a browser error when trying to navigate to the /blog route. The specific error message reads as follows: ./sanity.js:2:0 Module not found: Can't resolve '@sanity ...

Utilize restrictions (type/interface) on data types

When working with TypeScript, I am creating a type that consists of unions of other types: type A = B | C | D // ... One important requirement is that I want to ensure at compile time that B, C, D, and so on, have the structure of a generic template to en ...

Guide on invoking child components' functions from the parent component in Angular 6

Main Component import { Component } from '@angular/core'; import { DisplayComponent } from './display.component'; @Component({ selector: 'my-app', template: ` <button (click)="submit()">Call Child Com ...

I'm encountering an issue in my node application where it is unable to access the push

I am a beginner in the world of node.js and express. Whenever I try to start my application using the command npm start, I encounter an error message saying Cannot Read property push of undefined from my index.js file. The problematic code snippet looks l ...

Utilizing React with Typescript to access specific props

I am a newcomer to React and TypeScript and I am facing a challenge while trying to enhance an existing component by creating a wrapper around it. The issue I am encountering is related to adding my custom values to the properties. My goal is to extend th ...

Encountering a NgForm provider error in Angular 4.4.6 development mode

UPDATE: Identifying the root of the issue has led me to search for a suitable solution. NOTE: This complication is specific to development mode (not production, and not utilizing AOT). The "Update" resolution I am implementing can be found here. In an a ...

"Trouble accessing the URL" error encountered when trying to load templateUrl for dynamic components in Angular 2

Attempted to modify a solution found here. The modification works well, but when changing the template to templateUrl in the component that needs to be loaded dynamically, an error occurs: "No ResourceLoader implementation has been provided. Can't rea ...

How to access elements by their class name in Angular

Recently, I encountered a situation with this specific span element: <span *ngFor="let list of lists[0].question; let i = index" id="word{{ i }}" (click)="changestyle($event)" class="highlight"> {{ list}} < ...

Verify the data type of the returned information from the graphql query

Within my code, I am utilizing a graphql query through a hook that has been automatically generated by Codegen. Codegen not only creates return types but also all data types required. According to the types defined by codegen, the expected return type of m ...

The Observable pipeline is typically void until it undergoes a series of refreshing actions

The issue with the observable$ | async; else loading; let x condition usually leads to staying in the loading state, and it requires multiple refreshes in the browser for the data to become visible. Below is the code snippet that I utilized: // TypeScript ...

Issue with displaying data using a custom pure pipe and boolean in ngIf condition

For my current web project, I require a friendship/follow feature. There are two roles involved: admins and regular users. Regular users have the ability to follow other users, while admins do not possess this capability. When a user wishes to follow anot ...

Error with Array type encountered in Typescript's find method

I am encountering an issue with code that looks like this: type X = { test: number; x: number; }[]; type Y = { test: number; y: number; }[]; type Z = { test: number; z: number; }[]; export const testFunc = (arg: X | Y | Z) => { return a ...

Converting an array of objects to an array of JSON objects in TypeScript

My dilemma lies in the data I have uploaded under the _attachments variable: https://i.sstatic.net/jnFNH.png My aim is to format this data for insertion in the following structure: "_attachments": [ { "container": "string", "fileName": "string" ...

The specified 'detail' property cannot be found on the given type '{}'. Error code: 2339

I encountered the error mentioned in the title while working on the code below. Any suggestions on how to resolve this issue? Any assistance would be greatly appreciated! import { useHistory } from "react-router-dom"; let h ...

Angular 15 is unfortunately not compatible with my current data consumption capabilities

I'm currently facing an issue with Angular 15 where I am trying to access the "content" element within a JSON data. However, when attempting to retrieve the variable content, I am unable to view the elements it contains. import { Component, OnInit } ...

Issue with Jest: receiving error message "Module cannot be found" despite having the package installed

Recently, I went through a cleanup and update process for a private package to make it compatible with Vite. Initially, the package.json file had the following structure: { "name": "@myRegistry/my-package", "version": &qu ...