diff --git a/.changeset/sharp-ducks-travel.md b/.changeset/sharp-ducks-travel.md new file mode 100644 index 0000000..8b34671 --- /dev/null +++ b/.changeset/sharp-ducks-travel.md @@ -0,0 +1,5 @@ +--- +"@cuppachino/proxy-fn": patch +--- + +add test cases and fix bug with nested Promise types diff --git a/src/modules/proxy-fn.ts b/src/modules/proxy-fn.ts index c1f9664..1d214f2 100644 --- a/src/modules/proxy-fn.ts +++ b/src/modules/proxy-fn.ts @@ -1,5 +1,8 @@ import type { Assigned } from '../types/assigned.js' +import type { DefinitelyAwaited } from '../types/definitely-awaited.js' +import type { If } from '../types/if.js' import type { IsEmpty } from '../types/is-empty.js' +import type { IsPromise } from '../types/is-promise.js' import type { MaybePromise } from '../types/maybe-promise.js' import { isPromise } from '../utils/is-promise.js' @@ -14,7 +17,7 @@ export type ProxyFnHandler any> = < }> ) => ProxyFn< NewArgs, - [ActualArgs] extends [Promise] ? Promise : NewReturnType, + If, Promise>, NewReturnType>, Assigned > diff --git a/src/types/definitely-awaited.ts b/src/types/definitely-awaited.ts new file mode 100644 index 0000000..5f86ed6 --- /dev/null +++ b/src/types/definitely-awaited.ts @@ -0,0 +1 @@ +export type DefinitelyAwaited = T extends Promise ? U : T diff --git a/src/types/if.ts b/src/types/if.ts new file mode 100644 index 0000000..42e69b4 --- /dev/null +++ b/src/types/if.ts @@ -0,0 +1 @@ +export type If = T extends true ? A : B \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 662662a..5a31f8f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,7 @@ -export type * from './is-never.js' -export type * from './is-empty.js' -export type * from './maybe-promise.js' export type * from './assigned.js' +export type * from './definitely-awaited.js' +export type * from './if.js' +export type * from './is-empty.js' +export type * from './is-never.js' +export type * from './is-promise.js' +export type * from './maybe-promise.js' \ No newline at end of file diff --git a/src/types/is-promise.ts b/src/types/is-promise.ts new file mode 100644 index 0000000..849c6d7 --- /dev/null +++ b/src/types/is-promise.ts @@ -0,0 +1 @@ +export type IsPromise = T extends Promise ? true : false \ No newline at end of file diff --git a/tests/proxy-fn.test.ts b/tests/proxy-fn.test.ts index bc736e7..86fd27d 100644 --- a/tests/proxy-fn.test.ts +++ b/tests/proxy-fn.test.ts @@ -4,7 +4,7 @@ import { test, expectTypeOf, expect } from 'vitest' test('proxy function', () => { const add = (a: number, b: number) => a + b - + expectTypeOf(add).toEqualTypeOf((a: number, b: number) => a + b) expectTypeOf(add).not.toEqualTypeOf<(...args: number[]) => number>() @@ -84,12 +84,17 @@ test('proxy function', () => { }) test('proxy function with properties', () => { - const adder = Object.assign(function _adder(a: number, b: number) { return a + b }, { - hello: 'addAlt', - greet() { - console.log(this.hello) + const adder = Object.assign( + function _adder(a: number, b: number) { + return a + b + }, + { + hello: 'addAlt', + greet() { + console.log(this.hello) + } } - }) + ) const b1 = proxyFn(adder)({}) // ^? @@ -108,12 +113,74 @@ test('proxy function with properties', () => { return res } }) - expectTypeOf(b2).toEqualTypeOf<(() => Promise) & { - hello: string, - greet(): void - }>() + expectTypeOf(b2).toEqualTypeOf< + (() => Promise) & { + hello: string + greet(): void + } + >() expect(b2.hello).toBe('addAlt') expect(b2.greet).toBeInstanceOf(Function) expect(b2()).instanceOf(Promise) expect(b2()).resolves.toBe(3) -}) \ No newline at end of file +}) + +test('should not return nested promises', () => { + const getInventories = (id: number) => + Promise.resolve({ + ownerId: id, + items: [] + }) + const getDefaultUser = () => Promise.resolve({ id: 42069 }) + + const x = proxyFn(getInventories)( + (() => { + let currentId: number | null = null + const getId = async () => { + if (currentId !== null) return currentId + return (currentId = (await getDefaultUser()).id) + } + return { + async from() { + return [await getId()] + }, + async to(res) { + return [await res, 'closure'] as const + } + } + })() + ) + expectTypeOf(x).toEqualTypeOf< + () => Promise + >() + expect(x()).instanceOf(Promise) + expect(x()).resolves.toHaveLength(2) + expect(x()).resolves.toEqual([{ ownerId: 42069, items: [] }, 'closure']) + + const y = proxyFn(getInventories)( + (() => { + const { getId } = new (class { + currentId: number | null = null + getId = async () => { + if (this.currentId !== null) return this.currentId + return (this.currentId = (await getDefaultUser()).id) + } + })() + return { + async from() { + return [await getId()] + }, + async to(res) { + return [await res, 'class'] as const + } + } + })() + ) + + expectTypeOf(y).toEqualTypeOf< + () => Promise + >() + expect(y()).instanceOf(Promise) + expect(y()).resolves.toHaveLength(2) + expect(y()).resolves.toEqual([{ ownerId: 42069, items: [] }, 'class']) +})