#Pick only async function from interface

36 messages · Page 1 of 1 (latest)

agile geyser
#

Hi, is there a way to extract only the async function from an interface such as:

interface API {
    foo: string;
    (path: string): Promise<boolean>; // <- I want to only keep function signatures that return promises
    (path: string, callback: (err: Error, exists: boolean) => void): void;
}
vast wharf
#

!hb key remapping

sharp crowBOT
vast wharf
#

oh wait

#

signatures?

#

i don't think it's possible to manipulate function signatures

sharp crowBOT
vast wharf
#

!ts

sharp crowBOT
#
interface API {
    foo: string;
    aaa(path: string): Promise<boolean>; // <- I want to only keep function signatures that return promises
    bbb(path: string, callback: (err: Error, exists: boolean) => void): void;
}

type DoTheThing<T> = {
    [K in keyof T as T[K] extends (...args: any[]) => Promise<any> ? K : never]: T[K];
}

type What = DoTheThing<API>;
//   ^? - type What = {
//       aaa: (path: string) => Promise<boolean>;
//   }```
agile geyser
#

Yep it's function signatures, I am trying to extract the async version of an overloaded function

vast wharf
#

oh

#

yeah i don't think that's possible, i'm afraid

agile geyser
#

My use case is that I am trying to type a mock from Jest of a function that has an overloaded type signature (fs-extra.pathExists) and I don't want to repeat the type signature in my code but rather infer it from the lib

#

but I wasn't able to figure that one out

sharp crowBOT
vast wharf
#

!ts

sharp crowBOT
#
interface API {
    foo: string;
    (path: string): Promise<boolean>;
    (path: string, callback: (err: Error, exists: boolean) => void): string;
}

type DoTheThing<T> = T extends ((...args: infer Args) => Promise<infer R>) ? (...args: Args) => Promise<R> : never;

type What = DoTheThing<API>;
//   ^? - type What = (path: string, callback: (err: Error, exists: boolean) => void) => Promise<unknown>```
vast wharf
#

typescript is doing something funny here

#

definitely not what you want though

agile geyser
#

I think the interface API is misleading though

#

it's more what's happening

#
pathExists: {
    (path: string): Promise<boolean>;
    (path: string, callback: (err: Error, exists: boolean) => void): void;
} & jest.MockWithArgs<{
    (path: string): Promise<boolean>;
    (path: string, callback: (err: Error, exists: boolean) => void): void;
}> & {} & {}
#

and I want indicate that I want to use the (path: string): Promise<boolean>; variant as a mock

vast wharf
#

overloads in general aren't possible to pull apart, i don't think

agile geyser
#

seems like you are right

vast wharf
#

you could try specifying (path: string) => Promise<boolean> as a generic parameter manually

#

if that's an option

agile geyser
#

I am actually doing something like this: const mockedPathExists = fs.pathExists as jest.MockedFunction<(path: string) => Promise<boolean>>;

#

It works but if fs changes it's type signature I will have a mismatch in types signature

vast wharf
#

is fs.pathExists actually mocked though

agile geyser
#

yup because I have some global mock of the whole module

vast wharf
#

ah

#

yeah unfortunately i don't think there's a better solution

agile geyser
#

TBH I am still discovering how to type Jest tests correctly, but we have a lot of type assertions right now such as (fs.rm as jest.Mock).mockReturnValue(true)

#

when fs.rm returns either void or Promise<void>