8000 New: Add TypeScript definitions (closes #41) (#47) · gulpjs/async-done@d79bddf · GitHub
[go: up one dir, main page]

Skip to content

Commit d79bddf

Browse files
demurgosphated
authored andcommitted
New: Add TypeScript definitions (closes #41) (#47)
1 parent bc08f06 commit d79bddf

File tree

11 files changed

+348
-9
lines changed

11 files changed

+348
-9
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ Optionally takes a callback to call when async tasks are complete.
6767
* `Promise` returned
6868
- Completion: [onFulfilled][promise-onfulfilled] method called
6969
- Error: [onRejected][promise-onrejected] method called
70-
* `Observable` returned
71-
- Completion: [onCompleted][observable-subscribe] method called
72-
- Error: [onError][observable-subscribe] method called
70+
* `Observable` (e.g. from [RxJS v5][rxjs5-observable] or [RxJS v4][rxjs5-observable]) returned
71+
- Completion: [complete][rxjs5-subscriber-complete] method called
72+
- Error: [error][rxjs5-subscriber-error] method called
7373

7474
__Warning:__ Sync tasks are __not supported__ and your function will never complete if the one of the above strategies is not used to signal completion. However, thrown errors will be caught by the domain.
7575

@@ -96,7 +96,10 @@ MIT
9696
[event-stream]: https://github.com/dominictarr/event-stream
9797
[promise-onfulfilled]: http://promisesaplus.com/#point-26
9898
[promise-onrejected]: http://promisesaplus.com/#point-30
99-
[observable-subscribe]: https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md
99+
[rx4-observable]: https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md
100+
[rxjs5-observable]: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html
101+
[rxjs5-observer-complete]: http://reactivex.io/rxjs/class/es6/MiscJSDoc.js~ObserverDoc.html#instance-method-complete
102+
[rxjs5-observer-error]: http://reactivex.io/rxjs/class/es6/MiscJSDoc.js~ObserverDoc.html#instance-method-error
100103

101104
[downloads-image]: http://img.shields.io/npm/dm/async-done.svg
102105
[npm-url]: https://www.npmjs.com/package/async-done

index.d.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Notes about these type definitions:
3+
*
4+
* - Callbacks returning multiple completion values using multiple arguments are not supported by these types.
5+
* Prefer to use Node's style by grouping your values in a single object or array.
6+
* Support for this kind of callback is blocked by Microsoft/TypeScript#5453
7+
*
8+
* - For ease of use, `asyncDone` lets you pass callback functions with a result type `T` instead of `T | undefined`.
9+
* This matches Node's types but can lead to unsound code being typechecked.
10+
*
11+
* The following code typechecks but fails at runtime:
12+
* ```typescript
13+
* async function getString(): Promise<string> {
14+
* return "Hello, World!";
15+
* }
16+
*
17+
* async function evilGetString(): Promise<string> {
18+
* throw new Error("Hello, World!");
19+
* }
20+
*
21+
* function cb(err: Error | null, result: string): void {
22+
* // This is unsound because `result` is `undefined` when `err` is not `null`.
23+
* console.log(result.toLowerCase());
24+
* }
25+
*
26+
* asyncDone(getString, cb); // Prints `hello, world!`
27+
* asyncDone(evilGetString, cb); // Runtime error: `TypeError: Cannot read property 'toLowerCase' of undefined`
28+
* ```
29+
*
30+
* Enforcing stricter callbacks would require developers to use `result?: string` and assert the existence
31+
* of the result either by checking it directly or using the `!` assertion operator after testing for errors.
32+
* ```typescript
33+
* function stricterCb1(err: Error | null, result?: string): void {
34+
* if (err !== null) {
35+
* console.error(err);
36+
* return;
37+
* }
38+
* console.log(result!.toLowerCase());
39+
* }
40+
*
41+
* function stricterCb2(err: Error | null, result?: string): void {
42+
* if (result === undefined) {
43+
* console.error("Undefined result. Error:);
44+
* console.error(err);
45+
* return;
46+
* }
47+
* console.log(result.toLowerCase());
48+
* }
49+
* ```
50+
*/
51+
import { ChildProcess } from "child_process";
52+
import { EventEmitter } from "events";
53+
import { Stream } from "stream";
54+
55+
declare namespace asyncDone {
56+
57+
/**
58+
* Represents a callback function used to signal the completion of a
59+
* task without any result value.
60+
*/
61+
type VoidCallback = (err: Error | null) => void;
62+
63+
/**
64+
* Represents a callback function used to signal the completion of a
65+
* task with a single result value.
66+
*/
67+
interface Callback<T> {
68+
(err: null, result: T): void;
69+
70+
// Use `result?: T` or `result: undefined` to require the consumer to assert the existence of the result
71+
// (even in case of success). See comment at the top of the file.
72+
(err: Error, result?: any): void;
73+
}
74+
75+
/**
76+
* Minimal `Observable` interface compatible with `async-done`.
77+
*
78+
* @see https://github.com/ReactiveX/rxjs/blob/c3c56867eaf93f302ac7cd588034c7d8712f2834/src/internal/Observable.ts#L77
79+
*/
80+
interface Observable<T = any> {
81+
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): any;
82+
}
83+
84+
/**
85+
* Represents an async operation.
86+
*/
87+
export type AsyncTask<R = any> =
88+
((done: VoidCallback) => void)
89+
| ((done: Callback<R>) => void)
90+
| (() => ChildProcess | EventEmitter | Observable<R> | PromiseLike<R> | Stream);
91+
}
92+
93+
/**
94+
* Takes a function to execute (`fn`) and a function to call on completion (`callback`).
95+
*
96+
* @param fn Function to execute.
97+
* @param callback Function to call on completion.
98+
*/
99+
declare function asyncDone<R = any>(fn: asyncDone.AsyncTask<R>, callback: asyncDone.Callback<R>): void;
100+
101+
export = asyncDone;

package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@
66
"contributors": [
77
"Blaine Bublitz <blaine.bublitz@gmail.com>",
88
"Pawel Kozlowski <pkozlowski.opensource@gmail.com>",
9-
"Matthew Podwysocki <matthew.podwysocki@gmail.com>"
9+
"Matthew Podwysocki <matthew.podwysocki@gmail.com>",
10+
"Charles Samborski <demurgos@demurgos.net>"
1011
],
1112
"repository": "gulpjs/async-done",
1213
"license": "MIT",
1314
"engines": {
1415
"node": ">= 0.10"
1516
},
1617
"main": "index.js",
18+
"types": "index.d.ts",
1719
"files": [
1820
"index.js",
1921
"LICENSE"
2022
],
2123
"scripts": {
2224
"lint": "eslint . && jscs index.js test/",
2325
"pretest": "npm run lint",
24-
"test": "mocha --async-only",
26+
"test": "mocha --async-only && npm run test-types",
27+
"test-types": "tsc -p test/types",
2528
"cover": "istanbul cover _mocha --report lcovonly",
2629
"coveralls": "npm run cover && istanbul-coveralls"
2730
},
@@ -32,6 +35,7 @@
3235
"stream-exhaust": "^1.0.1"
3336
},
3437
"devDependencies": {
38+
"@types/node": "^9.3.0",
3539
"eslint": "^1.7.3",
3640
"eslint-config-gulp": "^2.0.0",
3741
"expect": "^1.19.0",
@@ -41,8 +45,9 @@
4145
"jscs-preset-gulp": "^1.0.0",
4246
"mocha": "^2.4.5",
4347
"pumpify": "^1.3.6",
44-
"rx": "^4.0.6",
48+
"rxjs": "^5.5.6",
4549
"through2": "^2.0.0",
50+
"typescript": "^2.6.2",
4651
"when": "^3.7.3"
4752
},
4853
"keywords": [

test/observables.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ var expect = require('expect');
44

55
var asyncDone = require('../');
66

7-
var Observable = require('rx').Observable;
7+
var Observable = require('rxjs').Observable;
88

99
function success() {
1010
return Observable.empty();
1111
}
1212

1313
function successValue() {
14-
return Observable.return(42);
14+
// This corresponds to `Observable.return(42);` in RxJS 4
15+
return Observable.of(42);
1516
}
1617

1718
function failure() {

test/types/callback.ts

Lines changed: 44 additions & 0 deletions
Origina F438 l file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import asyncDone, { Callback } from "async-done";
2+
3+
function success(cb: Callback<number>): void {
4+
cb(null, 2);
5+
}
6+
7+
function failure(cb: Callback<number>): void {
8+
cb(new Error("Callback Error"));
9+
}
10+
11+
function neverDone(): number {
12+
return 2;
13+
}
14+
15+
// `success` and stricter callback
16+
asyncDone(success, function (err: Error | null, result?: number): void {
17+
console.log("Done");
18+
});
19+
20+
// The following code fails to compile as expected:
21+
// asyncDone(success, function (err: Error | null, result?: string): void {
22+
// console.log("Done");
23+
// });
24+
25+
// `success` and unsound callback
26+
asyncDone(success, function (err: Error | null, result: number): void {
27+
console.log("Done");
28+
});
29+
30+
// `failure` and stricter callback
31+
asyncDone(failure, function (err: Error | null, result?: number): void {
32+
console.log("Done");
33+
});
34+
35+
// `failure` and unsound callback
36+
asyncDone(failure, function (err: Error | null, result: number): void {
37+
console.log("Done");
38+
});
39+
40+
// I don't think TS is currently able to prevent the current code from compiling
41+
// (`neverDone` matches with `(done: VoidCallback) => void` for example)
42+
// asyncDone(neverDone, function(err: Error | null, result?: number): void {
43+
// console.log("Done");
44+
// });

test/types/child_processes.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncDone from "async-done";
2+
import cp from "child_process";
3+
4+
5+
function success(): cp.ChildProcess {
6+
return cp.exec("echo hello world");
7+
}
8+
9+
function failure(): cp.ChildProcess {
10+
return cp.exec("foo-bar-baz hello world");
11+
}
12+
13+
asyncDone(success, function (err: Error | null): void {
14+
console.log("Done");
15+
});
16+
17+
asyncDone(failure, function (err: Error | null): void {
18+
console.log("Done");
19+
});

test/types/observables.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import asyncDone from "async-done";
2+
import { Observable } from "rxjs/Observable";
3+
import 'rxjs/add/observable/empty';
4+
import 'rxjs/add/observable/of';
5+
6+
function success(): Observable<undefined> {
7+
return Observable.empty();
8+
}
9+
10+
function successValue(): Observable<number> {
11+
return Observable.of(42);
12+
}
13+
14+
function failure(): Observable<number> {
15+
return Observable.throw(new Error("Observable error"));
16+
}
17+
18+
// `success` callback
19+
asyncDone(success, function (err: Error | null): void {
20+
console.log("Done");
21+
});
22+
23+
// The following code fails to compile as expected (`undefined` is not assignable to `number`):
24+
// asyncDone(success, function (err: Error | null, result: number): void {
25+
// console.log("Done");
26+
// });
27+
28+
// `successValue` and stricter callback
29+
asyncDone(successValue, function (err: Error | null, result?: number): void {
30+
console.log("Done");
31+
});
32+
33+
// `successValue` and unsound callback
34+
asyncDone(successValue, function (err: Error | null, result: number): void {
35+
console.log("Done");
36+
});
37+
38+
// `failure` and stricter callback
39+
asyncDone(failure, function (err: Error | null, result?: number): void {
40+
console.log("Done");
41+
});
42+
43+
// `failure` and unsound callback
44+
asyncDone(failure, function (err: Error | null, result: number): void {
45+
console.log("Done");
46+
});

test/types/promises.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import asyncDone from "async-done";
2+
3+
function success(): Promise<number> {
4+
return Promise.resolve(2);
5+
}
6+
7+
function failure(): Promise<number> {
8+
return Promise.reject(new Error("Promise Error"));
9+
}
10+
11+
// `successValue` and stricter callback
12+
asyncDone(success, function (err: Error | null, result?: number): void {
13+
console.log("Done");
14+
});
15+
16+
// The following code fails to compile as expected:
17+
// asyncDone(success, function (err: Error | null, result?: string): void {
18+
// console.log("Done");
19+
// });
20+
21+
// `successValue` and unsound callback
22+
asyncDone(success, function (err: Error | null, result: number): void {
23+
console.log("Done");
24+
});
25+
26+
// `failure` and stricter callback
27+
asyncDone(failure, function (err: Error | null, result?: number): void {
28+
console.log("Done");
29+
});
30+
31+
// `failure` and unsound callback
32+
asyncDone(failure, function (err: Error | null, result: number): void {
33+
console.log("Done");
34+
});

test/types/streams.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import asyncDone from "async-done";
2+
import { Readable, Stream } from "stream";
3+
4+
function streamSuccess(): Stream {
5+
return new Stream();
6+
}
7+
8+
function streamFail(): Stream {
9+
return new Stream();
10+
}
11+
12+
asyncDone(streamSuccess, function (err: Error | null): void {
13+
console.log("Done");
14+
});
15+
16+
asyncDone(streamFail, function (err: Error | null): void {
17+
console.log("Done");
18+
});

0 commit comments

Comments
 (0)
0