From 254fd7c1bb53f652ca76933696a1bc74fc2ed84e Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Tue, 3 Dec 2019 19:59:58 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Add=C2=A0`function=E2=80=91bind`=20(#39?= =?UTF-8?q?337)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add `function‑bind` * fix(function‑bind): Separate = 0.5; + +/** + * The `expectType` function from https://www.npmjs.com/package/tsd, + * except instead of returning `void`, it returns `T`. + */ +declare function expectType(value: T): T; + +// $ExpectType (thisArg: any, start?: number | undefined, end?: number | undefined) => any[] +const slice = expectType<(thisArg: any, start?: number, end?: number) => any[]>( + bind.call(Function.call, Array.prototype.slice), +); + +// $ExpectType (start?: number | undefined, end?: number | undefined) => any[] +const sliceBoundThis = expectType<(start?: number, end?: number) => any[]>( + bind.call(Function.call, Array.prototype.slice, null), +); + +// $ExpectType (end?: number | undefined) => any[] +const sliceBoundThisAndStart = expectType<(end?: number) => any[]>( + bind.call(Function.call, Array.prototype.slice, ['a'], 1), +); + +slice(['a']); + +// $ExpectType (...args: string[]) => boolean +expectType<(...args: string[]) => boolean>(bind.call(Boolean, null, String(), '2', '3', '4', '5')); + +// $ExpectType (...args: string[]) => boolean +expectType<(...args: string[]) => boolean>(bind.apply(Boolean, [null, '1', '2', '3', '4', '5'])); + +// Class compatibility: +class Foo { + constructor(public string: string, public number: number) {} +} + +// bind.call(): +// $ExpectType new (string: string, number: number) => Foo +bind.call(Foo, null); +// Foo.bind(null); + +// $ExpectType new (number: number) => Foo +bind.call(Foo, null, string); +// Foo.bind(null, string); + +// $ExpectType new () => Foo +bind.call(Foo, null, string, number); +// Foo.bind(null, string, number); + +// $ExpectType new () => Foo +bind.call(Foo, null, string, number, boolean); +// Foo.bind(null, string, number, boolean); + +// $ExpectType new () => Foo +bind.call(Foo, null, string, number, boolean, undefined); +// Foo.bind(null, string, number, boolean, undefined); + +// bind.apply(): +// $ExpectType new (string: string, number: number) => Foo +bind.apply(Foo, [null]); +// Foo.bind(...[null]); + +// $ExpectType new (number: number) => Foo +bind.apply(Foo, [null, string]); +// Foo.bind(...[null, string]); + +// $ExpectType new () => Foo +bind.apply(Foo, [null, string, number]); +// Foo.bind(...[null, string, number]); + +// $ExpectType new () => Foo +bind.apply(Foo, [null, string, number, boolean]); +// Foo.bind(...[null, string, number, boolean]); + +// $ExpectType new () => Foo +bind.apply(Foo, [null, string, number, boolean, undefined]); +// Foo.bind(...[null, string, number, boolean, undefined]); diff --git a/types/function-bind/implementation.d.ts b/types/function-bind/implementation.d.ts new file mode 100644 index 0000000000..4869987ac7 --- /dev/null +++ b/types/function-bind/implementation.d.ts @@ -0,0 +1,183 @@ +//#region bind(): +/** + * For a given function, creates a bound function that has the same body as the original function. + * The this object of the bound function is associated with the specified object, and has the specified initial parameters. + * @param thisArg The object to be used as the this object. + * @param args Arguments to bind to the parameters of the function. + */ +declare function bind(this: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R; +declare function bind( + this: (this: T, arg0: A0, ...args: A) => R, + thisArg: T, + arg0: A0, +): (...args: A) => R; +declare function bind( + this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, +): (...args: A) => R; +declare function bind( + this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, +): (...args: A) => R; +declare function bind( + this: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, +): (...args: A) => R; +declare function bind(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R; + +declare function bind(this: T, thisArg: any): T; +declare function bind( + this: new (arg0: A0, ...args: A) => R, + thisArg: any, + arg0: A0, +): new (...args: A) => R; +declare function bind( + this: new (arg0: A0, arg1: A1, ...args: A) => R, + thisArg: any, + arg0: A0, + arg1: A1, +): new (...args: A) => R; +declare function bind( + this: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: any, + arg0: A0, + arg1: A1, + arg2: A2, +): new (...args: A) => R; +declare function bind( + this: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: any, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, +): new (...args: A) => R; +declare function bind(this: new (...args: AX[]) => R, thisArg: any, ...args: AX[]): new (...args: AX[]) => R; +//#endregion + +declare namespace bind { + //#region bind.call(): + /** + * Creates a bound function with the specified object as the this value and the specified rest arguments as the arguments. + * @param thisArg The object to be used as the this object. + * @param args Argument values to be passed to the function. + */ + // CallableFunction: + function call(func: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R; + function call( + func: (this: T, arg0: A0, ...args: A) => R, + thisArg: T, + arg0: A0, + ): (...args: A) => R; + function call( + func: (this: T, arg0: A0, arg1: A1, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + ): (...args: A) => R; + function call( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, + ): (...args: A) => R; + function call( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, + ): (...args: A) => R; + function call(func: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R; + + // NewableFunction: + function call(func: new (...args: A) => R, thisArg: unknown): new (...args: A) => R; + function call( + func: new (arg0: A0, ...args: A) => R, + thisArg: unknown, + arg0: A0, + ): new (...args: A) => R; + function call( + func: new (arg0: A0, arg1: A1, ...args: A) => R, + thisArg: unknown, + arg0: A0, + arg1: A1, + ): new (...args: A) => R; + function call( + func: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: unknown, + arg0: A0, + arg1: A1, + arg2: A2, + ): new (...args: A) => R; + function call( + func: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: unknown, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, + ): new (...args: A) => R; + function call(func: new (...args: AX[]) => R, thisArg: unknown, ...args: AX[]): new (...args: AX[]) => R; + //#endregion + + //#region bind.apply(): + /** + * Creates a bound function with the specified object as the this value and the elements of specified array as the arguments. + * @param thisArg The object to be used as the this object. + * @param args An array of argument values to be passed to the function. + */ + // CallableFunction: + function apply(func: (this: T, ...args: A) => R, args: [T]): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, ...args: A) => R, + args: [T, A0], + ): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, arg1: A1, ...args: A) => R, + args: [T, A0, A1], + ): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + args: [T, A0, A1, A2], + ): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + args: [T, A0, A1, A2, A3], + ): (...args: A) => R; + function apply(func: (this: T, ...args: AX[]) => R, args: [T, ...AX[]]): (...args: AX[]) => R; + + // NewableFunction: + function apply(func: new (...args: A) => R, args: [unknown]): new (...args: A) => R; + function apply( + func: new (arg0: A0, ...args: A) => R, + args: [unknown, A0], + ): new (...args: A) => R; + function apply( + func: new (arg0: A0, arg1: A1, ...args: A) => R, + args: [unknown, A0, A1], + ): new (...args: A) => R; + function apply( + func: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + args: [unknown, A0, A1, A2], + ): new (...args: A) => R; + function apply( + func: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + args: [unknown, A0, A1, A2, A3], + ): new (...args: A) => R; + function apply(func: new (...args: AX[]) => R, args: [unknown, ...AX[]]): new (...args: AX[]) => R; + //#endregion +} + +export = bind; diff --git a/types/function-bind/index.d.ts b/types/function-bind/index.d.ts new file mode 100644 index 0000000000..4d97509f19 --- /dev/null +++ b/types/function-bind/index.d.ts @@ -0,0 +1,8 @@ +// Type definitions for function-bind 1.1 +// Project: https://github.com/Raynos/function-bind +// Definitions by: ExE Boss +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 3.0 + +import bind = require('./implementation'); +export = bind; diff --git a/types/function-bind/package.json b/types/function-bind/package.json new file mode 100644 index 0000000000..08cca52592 --- /dev/null +++ b/types/function-bind/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "types": "index", + "typesVersions": { + ">=3.3.0-0": { + "*": ["ts3.3/*"] + } + } +} diff --git a/types/function-bind/ts3.3/function-bind-tests.ts b/types/function-bind/ts3.3/function-bind-tests.ts new file mode 100644 index 0000000000..05e37f2c67 --- /dev/null +++ b/types/function-bind/ts3.3/function-bind-tests.ts @@ -0,0 +1,81 @@ +import bind = require('function-bind'); + +const string = String(Math.random()); +const number = Math.random(); +const boolean = Math.random() >= 0.5; + +/** + * The `expectType` function from https://www.npmjs.com/package/tsd, + * except instead of returning `void`, it returns `T`. + */ +declare function expectType(value: T): T; + +// $ExpectType (thisArg: any, start?: number | undefined, end?: number | undefined) => any[] +const slice = expectType<(thisArg: any, start?: number, end?: number) => any[]>( + bind.call(Function.call, Array.prototype.slice), +); + +// $ExpectType (start?: number | undefined, end?: number | undefined) => any[] +const sliceBoundThis = expectType<(start?: number, end?: number) => any[]>( + bind.call(Function.call, Array.prototype.slice, null), +); + +// $ExpectType (end?: number | undefined) => any[] +const sliceBoundThisAndStart = expectType<(end?: number) => any[]>( + bind.call(Function.call, Array.prototype.slice, ['a'], 1), +); + +slice(['a']); + +// $ExpectType (...args: string[]) => boolean +bind.call(Boolean, null, String(), '2', '3', '4', '5'); + +// $ExpectType (...args: string[]) => boolean +bind.apply(Boolean, [null, '1', '2', '3', '4', '5']); + +// Class compatibility: +class Foo { + constructor(public string: string, public number: number) {} +} + +// bind.call(): +// $ExpectType new (string: string, number: number) => Foo +bind.call(Foo, null); +// Foo.bind(null); + +// $ExpectType new (number: number) => Foo +bind.call(Foo, null, string); +// Foo.bind(null, string); + +// $ExpectType new () => Foo +bind.call(Foo, null, string, number); +// Foo.bind(null, string, number); + +// $ExpectType new () => Foo +bind.call(Foo, null, string, number, boolean); +// Foo.bind(null, string, number, boolean); + +// $ExpectType new () => Foo +bind.call(Foo, null, string, number, boolean, undefined); +// Foo.bind(null, string, number, boolean, undefined); + +// bind.apply(): +// $ExpectType new (string: string, number: number) => Foo +bind.apply(Foo, [null]); +// Foo.bind(...[null]); + +// $ExpectType new (number: number) => Foo +bind.apply(Foo, [null, string]); +// Foo.bind(...[null, string]); + +// $ExpectType new () => Foo +bind.apply(Foo, [null, string, number]); +// Foo.bind(...[null, string, number]); + +// $ExpectType new () => Foo +bind.apply(Foo, [null, string, number, boolean]); +// Foo.bind(...[null, string, number, boolean]); + +// $ExpectType new () => Foo +bind.apply(Foo, [null, string, number, boolean, undefined]); +// Foo.bind(...[null, string, number, boolean, undefined]); diff --git a/types/function-bind/ts3.3/implementation.d.ts b/types/function-bind/ts3.3/implementation.d.ts new file mode 100644 index 0000000000..43d093f598 --- /dev/null +++ b/types/function-bind/ts3.3/implementation.d.ts @@ -0,0 +1,183 @@ +//#region bind(): +/** + * For a given function, creates a bound function that has the same body as the original function. + * The this object of the bound function is associated with the specified object, and has the specified initial parameters. + * @param thisArg The object to be used as the this object. + * @param args Arguments to bind to the parameters of the function. + */ +declare function bind(this: T, thisArg: ThisParameterType): OmitThisParameter; +declare function bind( + this: (this: T, arg0: A0, ...args: A) => R, + thisArg: T, + arg0: A0, +): (...args: A) => R; +declare function bind( + this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, +): (...args: A) => R; +declare function bind( + this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, +): (...args: A) => R; +declare function bind( + this: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, +): (...args: A) => R; +declare function bind(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R; + +declare function bind(this: T, thisArg: any): T; +declare function bind( + this: new (arg0: A0, ...args: A) => R, + thisArg: any, + arg0: A0, +): new (...args: A) => R; +declare function bind( + this: new (arg0: A0, arg1: A1, ...args: A) => R, + thisArg: any, + arg0: A0, + arg1: A1, +): new (...args: A) => R; +declare function bind( + this: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: any, + arg0: A0, + arg1: A1, + arg2: A2, +): new (...args: A) => R; +declare function bind( + this: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: any, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, +): new (...args: A) => R; +declare function bind(this: new (...args: AX[]) => R, thisArg: any, ...args: AX[]): new (...args: AX[]) => R; +//#endregion + +declare namespace bind { + //#region bind.call(): + /** + * Creates a bound function with the specified object as the this value and the specified rest arguments as the arguments. + * @param thisArg The object to be used as the this object. + * @param args Argument values to be passed to the function. + */ + // CallableFunction: + function call(func: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R; + function call( + func: (this: T, arg0: A0, ...args: A) => R, + thisArg: T, + arg0: A0, + ): (...args: A) => R; + function call( + func: (this: T, arg0: A0, arg1: A1, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + ): (...args: A) => R; + function call( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, + ): (...args: A) => R; + function call( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: T, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, + ): (...args: A) => R; + function call(func: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R; + + // NewableFunction: + function call(func: new (...args: A) => R, thisArg: unknown): new (...args: A) => R; + function call( + func: new (arg0: A0, ...args: A) => R, + thisArg: unknown, + arg0: A0, + ): new (...args: A) => R; + function call( + func: new (arg0: A0, arg1: A1, ...args: A) => R, + thisArg: unknown, + arg0: A0, + arg1: A1, + ): new (...args: A) => R; + function call( + func: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + thisArg: unknown, + arg0: A0, + arg1: A1, + arg2: A2, + ): new (...args: A) => R; + function call( + func: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + thisArg: unknown, + arg0: A0, + arg1: A1, + arg2: A2, + arg3: A3, + ): new (...args: A) => R; + function call(func: new (...args: AX[]) => R, thisArg: unknown, ...args: AX[]): new (...args: AX[]) => R; + //#endregion + + //#region bind.apply(): + /** + * Creates a bound function with the specified object as the this value and the elements of specified array as the arguments. + * @param thisArg The object to be used as the this object. + * @param args An array of argument values to be passed to the function. + */ + // CallableFunction: + function apply(func: (this: T, ...args: A) => R, args: [T]): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, ...args: A) => R, + args: [T, A0], + ): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, arg1: A1, ...args: A) => R, + args: [T, A0, A1], + ): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + args: [T, A0, A1, A2], + ): (...args: A) => R; + function apply( + func: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + args: [T, A0, A1, A2, A3], + ): (...args: A) => R; + function apply(func: (this: T, ...args: AX[]) => R, args: [T, ...AX[]]): (...args: AX[]) => R; + + // NewableFunction: + function apply(func: new (...args: A) => R, args: [unknown]): new (...args: A) => R; + function apply( + func: new (arg0: A0, ...args: A) => R, + args: [unknown, A0], + ): new (...args: A) => R; + function apply( + func: new (arg0: A0, arg1: A1, ...args: A) => R, + args: [unknown, A0, A1], + ): new (...args: A) => R; + function apply( + func: new (arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, + args: [unknown, A0, A1, A2], + ): new (...args: A) => R; + function apply( + func: new (arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, + args: [unknown, A0, A1, A2, A3], + ): new (...args: A) => R; + function apply(func: new (...args: AX[]) => R, args: [unknown, ...AX[]]): new (...args: AX[]) => R; + //#endregion +} + +export = bind; diff --git a/types/function-bind/ts3.3/index.d.ts b/types/function-bind/ts3.3/index.d.ts new file mode 100644 index 0000000000..7dd504e8b2 --- /dev/null +++ b/types/function-bind/ts3.3/index.d.ts @@ -0,0 +1,2 @@ +import bind = require('./implementation'); +export = bind; diff --git a/types/function-bind/ts3.3/tsconfig.json b/types/function-bind/ts3.3/tsconfig.json new file mode 100644 index 0000000000..dc240faf2f --- /dev/null +++ b/types/function-bind/ts3.3/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es5"], + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "baseUrl": "../../", + "typeRoots": ["../../"], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "function-bind-tests.ts", + "index.d.ts" + ] +} diff --git a/types/function-bind/ts3.3/tslint.json b/types/function-bind/ts3.3/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/function-bind/ts3.3/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" } diff --git a/types/function-bind/tsconfig.json b/types/function-bind/tsconfig.json new file mode 100644 index 0000000000..ef7cdcf15b --- /dev/null +++ b/types/function-bind/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["esnext"], + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": ["../"], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "function-bind-tests.ts", + "index.d.ts" + ] +} diff --git a/types/function-bind/tslint.json b/types/function-bind/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/function-bind/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }