From 7b70fd96c8c8567a929118e544fc6d00b1f8b640 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Fri, 2 Sep 2022 18:56:10 +0700 Subject: [PATCH] Meta tweaks --- index.d.ts | 43 ++++++++++---------- index.test-d.ts | 102 ++++++++++++++++++++++++------------------------ package.json | 6 +-- readme.md | 14 +++---- tsconfig.json | 6 --- 5 files changed, 82 insertions(+), 89 deletions(-) delete mode 100644 tsconfig.json diff --git a/index.d.ts b/index.d.ts index 5f6c633..577fd09 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,40 +1,41 @@ /* eslint-disable @typescript-eslint/ban-types */ -type Last = T extends [...any, infer L] +type LastArrayElement = T extends [...any, infer L] ? L : never; -type DropLast = T extends [...(infer U), any] + +type DropLastArrayElement = T extends [...(infer U), unknown] ? U : []; type StringEndsWith = S extends `${infer _}${X}` ? true : false; -interface Options { +type Options = { multiArgs?: MultiArgs; include?: Includes; exclude?: Excludes; errorFirst?: ErrorFirst; promiseModule?: PromiseConstructor; excludeMain?: ExcludeMain; -} +}; -interface InternalOptions { +type InternalOptions = { multiArgs: MultiArgs; include: Includes; exclude: Excludes; errorFirst: ErrorFirst; -} +}; type Promisify> = ( - ...args: DropLast + ...args: DropLastArrayElement ) => -Last extends (...args: any) => any +LastArrayElement extends (...arguments_: any) => any // For single-argument functions when errorFirst: true we just return Promise as it will always reject. - ? Parameters> extends [infer SingleCallbackArg] ? GenericOptions extends {errorFirst: true} ? Promise : Promise + ? Parameters> extends [infer SingleCallbackArg] ? GenericOptions extends {errorFirst: true} ? Promise : Promise : Promise< GenericOptions extends {multiArgs: false} - ? Last>> - : Parameters> + ? LastArrayElement>> + : Parameters> > // Functions without a callback will return a promise that never settles. We model this as Promise : Promise; @@ -46,27 +47,27 @@ type PromisifyModule< Includes extends ReadonlyArray, Excludes extends ReadonlyArray, > = { - [K in keyof Module]: Module[K] extends (...args: infer Args) => any + [K in keyof Module]: Module[K] extends (...arguments_: infer Arguments) => any ? K extends Includes[number] - ? Promisify> + ? Promisify> : K extends Excludes[number] ? Module[K] : StringEndsWith extends true ? Module[K] - : Promisify> + : Promisify> : Module[K]; }; -declare function pify< - FirstArg, - Args extends readonly unknown[], +export default function pify< + FirstArgument, + Arguments extends readonly unknown[], MultiArgs extends boolean = false, ErrorFirst extends boolean = true, >( - input: (arg: FirstArg, ...args: Args) => any, + input: (argument: FirstArgument, ...arguments_: Arguments) => any, options?: Options<[], [], MultiArgs, ErrorFirst> -): Promisify<[FirstArg, ...Args], InternalOptions<[], [], MultiArgs, ErrorFirst>>; -declare function pify< +): Promisify<[FirstArgument, ...Arguments], InternalOptions<[], [], MultiArgs, ErrorFirst>>; +export default function pify< Module extends Record, Includes extends ReadonlyArray = [], Excludes extends ReadonlyArray = [], @@ -77,5 +78,3 @@ declare function pify< module: Module, options?: Options ): PromisifyModule; - -export = pify; diff --git a/index.test-d.ts b/index.test-d.ts index 4268720..d270705 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,5 +1,5 @@ import {expectError, expectType, printType} from 'tsd'; -import pify from '.'; +import pify from './index.js'; expectError(pify()); expectError(pify(null)); @@ -16,16 +16,16 @@ expectType>(pify((v: number) => {})()); expectType>(pify(() => 'hello')()); // Callback with 1 additional params -declare function fn1(x: number, fn: (error: Error, value: number) => void): void; -expectType>(pify(fn1)(1)); +declare function function1(x: number, function_: (error: Error, value: number) => void): void; +expectType>(pify(function1)(1)); // Callback with 2 additional params -declare function fn2(x: number, y: number, fn: (error: Error, value: number) => void): void; -expectType>(pify(fn2)(1, 2)); +declare function function2(x: number, y: number, function_: (error: Error, value: number) => void): void; +expectType>(pify(function2)(1, 2)); // Generics -declare function generic(value: T, fn: (error: Error, value: T) => void): void; +declare function generic(value: T, function_: (error: Error, value: T) => void): void; declare const genericValue: 'hello' | 'goodbye'; expectType>(pify(generic)(genericValue)); @@ -41,40 +41,40 @@ declare function generic10( value9: T9, value10: T10, cb: (error: Error, value: { - val1: T1; - val2: T2; - val3: T3; - val4: T4; - val5: T5; - val6: T6; - val7: T7; - val8: T8; - val9: T9; - val10: T10; + value1: T1; + value2: T2; + value3: T3; + value4: T4; + value5: T5; + value6: T6; + value7: T7; + value8: T8; + value9: T9; + value10: T10; }) => void ): void; expectType< Promise<{ - val1: 1; - val2: 2; - val3: 3; - val4: 4; - val5: 5; - val6: 6; - val7: 7; - val8: '8'; - val9: 9; - val10: 10; + value1: 1; + value2: 2; + value3: 3; + value4: 4; + value5: 5; + value6: 6; + value7: 7; + value8: '8'; + value9: 9; + value10: 10; }> >(pify(generic10)(1, 2, 3, 4, 5, 6, 7, '8', 9, 10)); // MultiArgs -declare function callback02(cb: (x: number, y: string) => void): void; -declare function callback12(value: 'a', cb: (x: number, y: string) => void): void; +declare function callback02(callback: (x: number, y: string) => void): void; +declare function callback12(value: 'a', callback: (x: number, y: string) => void): void; declare function callback22( value1: 'a', value2: 'b', - cb: (x: number, y: string) => void + callback: (x: number, y: string) => void ): void; expectType>(pify(callback02, {multiArgs: true})()); @@ -86,25 +86,25 @@ expectType>( ); // Overloads -declare function overloaded(value: number, cb: (error: Error, value: number) => void): void; -declare function overloaded(value: string, cb: (error: Error, value: string) => void): void; +declare function overloaded(value: number, callback: (error: Error, value: number) => void): void; +declare function overloaded(value: string, callback: (error: Error, value: string) => void): void; // Chooses last overload // See https://github.com/microsoft/TypeScript/issues/32164 expectType>(pify(overloaded)('')); declare const fixtureModule: { - method1: (arg: string, cb: (error: Error, value: string) => void) => void; - method2: (arg: number, cb: (error: Error, value: number) => void) => void; - method3: (arg: string) => string; - methodSync: (arg: 'sync') => 'sync'; - methodStream: (arg: 'stream') => 'stream'; - callbackEndingInSync: (arg: 'sync', cb: (error: Error, value: 'sync') => void) => void; - prop: number; + method1: (argument: string, callback: (error: Error, value: string) => void) => void; + method2: (argument: number, callback: (error: Error, value: number) => void) => void; + method3: (argument: string) => string; + methodSync: (argument: 'sync') => 'sync'; + methodStream: (argument: 'stream') => 'stream'; + callbackEndingInSync: (argument: 'sync', callback: (error: Error, value: 'sync') => void) => void; + property: number; }; // Module support -expectType(pify(fixtureModule).prop); +expectType(pify(fixtureModule).property); expectType>(pify(fixtureModule).method1('')); expectType>(pify(fixtureModule).method2(0)); // Same semantics as pify(fn) @@ -112,7 +112,7 @@ expectType>(pify(fixtureModule).method3()); // Excludes expectType< -(arg: string, cb: (error: Error, value: string) => void) => void +(argument: string, callback: (error: Error, value: string) => void) => void >(pify(fixtureModule, {exclude: ['method1']}).method1); // Includes @@ -121,26 +121,26 @@ expectType>(pify(fixtureModule, {include: ['method2']}).method2( // Excludes sync and stream method by default expectType< -(arg: 'sync') => 'sync' +(argument: 'sync') => 'sync' >(pify(fixtureModule, {exclude: ['method1']}).methodSync); expectType< -(arg: 'stream') => 'stream' +(argument: 'stream') => 'stream' >(pify(fixtureModule, {exclude: ['method1']}).methodStream); // Include sync method expectType< -(arg: 'sync') => Promise<'sync'> +(argument: 'sync') => Promise<'sync'> >(pify(fixtureModule, {include: ['callbackEndingInSync']}).callbackEndingInSync); // Option errorFirst: -declare function fn0(fn: (value: number) => void): void; +declare function function0(function_: (value: number) => void): void; // Unknown as it returns a promise that always rejects because errorFirst = true -expectType>(pify(fn0)()); -expectType>(pify(fn0, {errorFirst: true})()); +expectType>(pify(function0)()); +expectType>(pify(function0, {errorFirst: true})()); -expectType>(pify(fn0, {errorFirst: false})()); +expectType>(pify(function0, {errorFirst: false})()); expectType>(pify(callback02, {multiArgs: true, errorFirst: true})()); expectType>( pify(callback12, {multiArgs: true, errorFirst: false})('a'), @@ -152,9 +152,9 @@ expectType>( // Module function // eslint-disable-next-line @typescript-eslint/no-empty-function -function moduleFunction(_cb: (error: Error, value: number) => void): void {} +function moduleFunction(_callback: (error: Error, value: number) => void): void {} // eslint-disable-next-line @typescript-eslint/no-empty-function -moduleFunction.method = function (_cb: (error: Error, value: string) => void): void {}; +moduleFunction.method = function (_callback: (error: Error, value: string) => void): void {}; expectType>(pify(moduleFunction)()); @@ -163,8 +163,8 @@ expectType>(pify(moduleFunction, {excludeMain: true}).method()); // Classes declare class MyClass { - method1(cb: (error: Error, value: string) => void): void; - method2(arg: number, cb: (error: Error, value: number) => void): void; + method1(callback: (error: Error, value: string) => void): void; + method2(argument: number, callback: (error: Error, value: number) => void): void; } expectType>(pify(new MyClass()).method1()); diff --git a/package.json b/package.json index 62ad5d9..9113032 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "type": "module", "exports": "./index.js", + "types": "./index.d.ts", "engines": { "node": ">=14.16" }, @@ -44,11 +45,10 @@ "bluebird" ], "devDependencies": { - "ava": "^4.3.0", + "ava": "^4.3.3", "pinkie-promise": "^2.0.1", "tsd": "^0.23.0", - "typescript": "^4.8.2", "v8-natives": "^1.2.5", - "xo": "^0.49.0" + "xo": "^0.52.3" } } diff --git a/readme.md b/readme.md index adefb02..c655fe0 100644 --- a/readme.md +++ b/readme.md @@ -142,19 +142,19 @@ someClassPromisified.someFunction(); const someFunction = pify(someClass.someFunction.bind(someClass)); ``` -#### With TypeScript why is `pify` choosing the last function overload? +#### Why is `pify` choosing the last function overload when using it with TypeScript? -If you're using TypeScript and your input has [function overloads](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads) then only the last overload will be chosen and promisified. +If you're using TypeScript and your input has [function overloads](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads), then only the last overload will be chosen and promisified. -If you need to choose a different overload consider using a type assertion eg. +If you need to choose a different overload, consider using a type assertion: ```ts -function overloadedFunction(input: number, cb: (error: unknown, data: number => void): void -function overloadedFunction(input: string, cb: (error: unknown, data: string) => void): void - /* ... */ +function overloadedFunction(input: number, callback: (error: unknown, data: number => void): void +function overloadedFunction(input: string, callback: (error: unknown, data: string) => void): void { + /* … */ } -const fn = pify(overloadedFunction as (input: number, cb: (error: unknown, data: number) => void) => void) +const fn = pify(overloadedFunction as (input: number, callback: (error: unknown, data: number) => void) => void) // ^ ? (input: number) => Promise ``` diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 75cf960..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "esModuleInterop": true - } -}