Using Typescript to retrieve the Return Type of a function when called with specific Parameter types

My goal is to create 2 interfaces for accessing the database:

  • dao can be used by both admins and regular users, so each function needs an isAdmin:boolean parameter (e.g.
    updateUser(isAdmin: boolean, returnUser)
    )
  • daoAsAdmin, on the other hand, allows methods to be called without the isAdmin parameter (e.g. updateUser(returnUser))

Below is a snippet of code showcasing this:

type User = { name: string }

type DaoAsAdmin = {
    updateUser<ReturnUser extends boolean>(
        returnUser: ReturnUser
    ): ReturnUser extends true ? User : string
}

type Dao = {
    // injects `isAdmin` as first param of all dao methods
    [K in keyof DaoAsAdmin]: (isAdmin: boolean, ...params: Parameters<DaoAsAdmin[K]>) => ReturnType<DaoAsAdmin[K]>
}

// Real code implementation
const dao: Dao = {
    updateUser(isAdmin, returnUser) {
      throw 'not implemented'
    }
  }

// Using proxy trick to set isAdmin = true 
// as the first param of each dao method
const daoAsAdmin = new Proxy(dao, {
    get(target, prop, receiver) {
        return function (...params) {
            const NEW_PARAMS = [true, ...params]
            return target[prop](NEW_PARAMS)
        }
    },
}) as DaoAsAdmin

// Now, calling updateUser is simplified
const userAsAdmin = daoAsAdmin.updateUser(true) // returns type User
const userStringAsAdmin = daoAsAdmin.updateUser(false) // returns type string
// However, dao functions do not return the expected types
const user = dao.updateUser(false, true) // returns type string | User instead of just User
const userAsStr = dao.updateUser(false, false) // returns type string | User instead of just string

I have attempted various strategies but could not ensure that dao functions return the correct type. It appears that a combination of Parameters and ReturnType is needed, but there are no guidelines on using ReturnType with specified function parameters.

What modifications should I make to the Dao type definition to achieve the desired result?

The actual scenario is more complex and necessitates declaring types and constants separately. Please let me know if further clarification is required.

Typescript playground

Answer №1

Regrettably, TypeScript does not support the manipulation of generic function types at the type level. The language lacks higher kinded types as proposed in microsoft/TypeScript#1213. Even if such features were available, it's unclear how one would go about performing the desired type transformation.

Attempts to utilize conditional types like Parameters<T> or ReturnType<T> on generic functions result in the loss of generics, making it challenging to preserve them while manipulating types.


There is limited capability for modifying generic function types at the value level, demonstrated in microsoft/TypeScript#30125. With this approach, given a generic function type variable gf, another function hof() can be defined such that hof(gf) yields a related generic function type. For example:

function injectIsAdmin<A extends any[], R>(
    f: (...a: A) => R
): (isAdmin: boolean, ...a: A) => R {
    throw 0;
}

The method can be illustrated through an example:

const g = <T extends string, U extends number>(t: T, u: U) => [t, u] as const;

const gi = injectIsAdmin(g);

While helpful, this approach may not scale accordingly for mapped types as shown below:

function mapInjectAsAdmin<A extends Record<keyof R, any[]>, R extends Record<keyof A, any>>(
    f: { [K in keyof A]: (...args: A[K]) => R[K] } & { [K in keyof R]: (...args: A[K]) => R[K] }
): { [K in keyof A]: (isAdmin: boolean, ...args: A[K]) => R[K] {
    throw 0;
}

const badGi = mapInjectAsAdmin({ oops: g });

It becomes necessary to manually define Dao based on DaoAsAdmin by traversing all keys individually. This process involves utilizing a function to achieve the desired type computation, resulting in cumbersome manual coding complexities.

function daoTypeBuilder() {
    // Omitted sketchy code block due to character limit
 return dao;
}

type Dao = ReturnType<typeof daoTypeBuilder>;

Although achieving the required type definition is possible with meticulous effort, it raises questions about its practicality. Ultimately, the task demands convoluted workarounds and trickery rather than straightforward solutions.


In conclusion, accomplishing these manipulations without resorting to undesirable tactics proves to be a challenge within the current constraints of TypeScript.

Playground link to code

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

Creating a unique Angular 2 Custom Pipe tutorial

I've come across various instances of NG2 pipes online and decided to create one myself recently: @Pipe({name: 'planDatePipe'}) export class PlanDatePipe implements PipeTransform { transform(value: string): string { return sessionStor ...

When zooming out, Leaflet displays both tile layers

I'm currently working on integrating two tile layers along with a control for toggling between them. Below is the code snippet I am using: const layer1: L.TileLayer = L.tileLayer('http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', { ...

Getting Session from Next-Auth in API Route: A Step-by-Step Guide

When printing my session from Next Auth in a component like this, I can easily see all of its data. const session = useSession(); // ... <p>{JSON.stringify(session)}</p> I am facing an issue where I need to access the content of the session i ...

DiscordJS is throwing a TS2339 error stating that the property 'forEach' is not found on the type 'Collection<string, GuildMember>'

Upon attempting to utilize the code provided, I encountered the error messages Property 'forEach' does not exist on type 'Collection<string, GuildMember> and Property 'size' does not exist on type 'Collection<string, ...

Always deemed non-assignable but still recognized as a universal type?

I'm curious about why the never type is allowed as input in generic's extended types. For example: type Pluralize<A extends string> = `${A}s` type Working = Pluralize<'language'> // 'languages' -> Works as e ...

React Project Encounters NPM Installation Failure

I recently started delving into the world of React and experimenting with different examples. Everything was running smoothly until I attempted to start the server [npm start] and encountered an error as shown below. Despite my best efforts, I can't p ...

Creating a TypeScript type or interface that represents an object with one of many keys or simply a string

I am tasked with creating an interface that can either be a string or an object with one of three specific keys. The function I have takes care of different errors and returns the appropriate message: export const determineError = (error: ServerAlerts): ...

Employing a provider within a different provider and reciprocally intertwining their functions

I'm currently facing an issue with two providers, which I have injected through the constructor. Here's the code for my user-data.ts file: @Injectable() export class UserDataProvider { constructor(private apiService: ApiServiceProvider) { ...

Typescript void negation: requiring functions to not return void

How can I ensure a function always returns a value in TypeScript? Due to the fact that void is a subtype of any, I haven't been able to find any generics that successfully exclude void from any. My current workaround looks like this: type NotVoid ...

Using react-confetti to create numerous confetti effects simultaneously on a single webpage

I'm looking to showcase multiple confetti effects using the react-confetti library on a single page. However, every attempt to do so in my component seems to only display the confetti effect on the last element, rather than all of them. The canvas fo ...

The argument of type 'NextRouter' cannot be assigned to the parameter of type 'Props' in this scenario

In my component, I am initializing a Formik form by calling a function and passing the next/router object. This is how it looks: export default function Reset() { const router = useRouter(); const formik = useFormik(RecoverForm(router)); return ( ...

Transform the property of type any/unknown into a specific generic type T within the map

Suppose I start with... type TypeNonGeneric = { prop1: any, prop2: string }; How do I transform it into... type TypeGeneric<T> = { prop1: T, prop2: string }; I have reviewed the documentation and it appears that I need to create a new generic type ...

What is the best way to switch the CSS class of a single element with a click in Angular 2

When I receive data from an API, I am showcasing specific items for female and male age groups on a webpage using the code snippet below: <ng-container *ngFor="let event of day.availableEvents"> {{ event.name }} <br> <n ...

A conditional type used with an array to return either an Error object or a generic type when the array is destructured

Within my Typescript project, I've implemented a Result type to be returned from functions, containing either an error or some data. This can take the form of [Error, null], or [null, Data]. Here's an example: type Result<Data> = [ Error | ...

The custom form input in Angular2 is throwing an error because it is trying to access the property 'name' of an

Upon switching to the latest Angular version 2 final, I encountered the following error Uncaught TypeError: Cannot read property 'name' of undefined This is my customized input import { Component, EventEmitter, Provider, forwardRef } from &a ...

Why is webpack attempting to package up my testing files?

In my project, I have two main directories: "src" and "specs". The webpack configuration entrypoint is set to a file within the src directory. Additionally, the context of the webpack config is also set to the src directory. There is a postinstall hook in ...

Encountering compilation errors with TypeScript files in Angular 2 while using Visual Studio 2017

Currently, I am in the process of developing an Angular 2 application Here is a snippet of the TypeScript code that I have written: import { Component } from 'angular2/core'; @Component({ selector: 'my-app', template: ' &l ...

A problem arises when the React effect hook fails to trigger while utilizing React Context

I have created a component that is supposed to generate different pages (one for each child) and display only the selected page: import * as React from "react"; export interface SwitchProps { pageId: number; children: React.ReactChild[]; } ...

ngModelChange doesn't trigger if the value is manually altered

Here is the scenario I am experiencing: //html <input (ngModelChange)="onSelection()" [(ngModel)]="selectedNode" > // in the ts file onSelection() { alert('changed'); } Typing something inside the input tri ...

There is no property called 'x' in type 'y'

Can anyone explain why TypeScript is telling me this: Property 'dateTime' does not exist on type 'SSRPageProps'.ts(2339) Looking at my code below, I have data-time typed. import React from "react"; import axios from "axi ...