-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
11 changed files
with
348 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/** | ||
* Notes about these type definitions: | ||
* | ||
* - Callbacks returning multiple completion values using multiple arguments are not supported by these types. | ||
* Prefer to use Node's style by grouping your values in a single object or array. | ||
* Support for this kind of callback is blocked by Microsoft/TypeScript#5453 | ||
* | ||
* - For ease of use, `asyncDone` lets you pass callback functions with a result type `T` instead of `T | undefined`. | ||
* This matches Node's types but can lead to unsound code being typechecked. | ||
* | ||
* The following code typechecks but fails at runtime: | ||
* ```typescript | ||
* async function getString(): Promise<string> { | ||
* return "Hello, World!"; | ||
* } | ||
* | ||
* async function evilGetString(): Promise<string> { | ||
* throw new Error("Hello, World!"); | ||
* } | ||
* | ||
* function cb(err: Error | null, result: string): void { | ||
* // This is unsound because `result` is `undefined` when `err` is not `null`. | ||
* console.log(result.toLowerCase()); | ||
* } | ||
* | ||
* asyncDone(getString, cb); // Prints `hello, world!` | ||
* asyncDone(evilGetString, cb); // Runtime error: `TypeError: Cannot read property 'toLowerCase' of undefined` | ||
* ``` | ||
* | ||
* Enforcing stricter callbacks would require developers to use `result?: string` and assert the existence | ||
* of the result either by checking it directly or using the `!` assertion operator after testing for errors. | ||
* ```typescript | ||
* function stricterCb1(err: Error | null, result?: string): void { | ||
* if (err !== null) { | ||
* console.error(err); | ||
* return; | ||
* } | ||
* console.log(result!.toLowerCase()); | ||
* } | ||
* | ||
* function stricterCb2(err: Error | null, result?: string): void { | ||
* if (result === undefined) { | ||
* console.error("Undefined result. Error:); | ||
* console.error(err); | ||
* return; | ||
* } | ||
* console.log(result.toLowerCase()); | ||
* } | ||
* ``` | ||
*/ | ||
import { ChildProcess } from "child_process"; | ||
import { EventEmitter } from "events"; | ||
import { Stream } from "stream"; | ||
|
||
declare namespace asyncDone { | ||
|
||
/** | ||
* Represents a callback function used to signal the completion of a | ||
* task without any result value. | ||
*/ | ||
type VoidCallback = (err: Error | null) => void; | ||
|
||
/** | ||
* Represents a callback function used to signal the completion of a | ||
* task with a single result value. | ||
*/ | ||
interface Callback<T> { | ||
(err: null, result: T): void; | ||
|
||
// Use `result?: T` or `result: undefined` to require the consumer to assert the existence of the result | ||
// (even in case of success). See comment at the top of the file. | ||
(err: Error, result?: any): void; | ||
} | ||
|
||
/** | ||
* Minimal `Observable` interface compatible with `async-done`. | ||
* | ||
* @see https://github.com/ReactiveX/rxjs/blob/c3c56867eaf93f302ac7cd588034c7d8712f2834/src/internal/Observable.ts#L77 | ||
*/ | ||
interface Observable<T = any> { | ||
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): any; | ||
} | ||
|
||
/** | ||
* Represents an async operation. | ||
*/ | ||
export type AsyncTask<R = any> = | ||
((done: VoidCallback) => void) | ||
| ((done: Callback<R>) => void) | ||
| (() => ChildProcess | EventEmitter | Observable<R> | PromiseLike<R> | Stream); | ||
} | ||
|
||
/** | ||
* Takes a function to execute (`fn`) and a function to call on completion (`callback`). | ||
* | ||
* @param fn Function to execute. | ||
* @param callback Function to call on completion. | ||
*/ | ||
declare function asyncDone<R = any>(fn: asyncDone.AsyncTask<R>, callback: asyncDone.Callback<R>): void; | ||
|
||
export = asyncDone; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import asyncDone, { Callback } from "async-done"; | ||
|
||
function success(cb: Callback<number>): void { | ||
cb(null, 2); | ||
} | ||
|
||
function failure(cb: Callback<number>): void { | ||
cb(new Error("Callback Error")); | ||
} | ||
|
||
function neverDone(): number { | ||
return 2; | ||
} | ||
|
||
// `success` and stricter callback | ||
asyncDone(success, function (err: Error | null, result?: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// The following code fails to compile as expected: | ||
// asyncDone(success, function (err: Error | null, result?: string): void { | ||
// console.log("Done"); | ||
// }); | ||
|
||
// `success` and unsound callback | ||
asyncDone(success, function (err: Error | null, result: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// `failure` and stricter callback | ||
asyncDone(failure, function (err: Error | null, result?: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// `failure` and unsound callback | ||
asyncDone(failure, function (err: Error | null, result: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// I don't think TS is currently able to prevent the current code from compiling | ||
// (`neverDone` matches with `(done: VoidCallback) => void` for example) | ||
// asyncDone(neverDone, function(err: Error | null, result?: number): void { | ||
// console.log("Done"); | ||
// }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import asyncDone from "async-done"; | ||
import cp from "child_process"; | ||
|
||
|
||
function success(): cp.ChildProcess { | ||
return cp.exec("echo hello world"); | ||
} | ||
|
||
function failure(): cp.ChildProcess { | ||
return cp.exec("foo-bar-baz hello world"); | ||
} | ||
|
||
asyncDone(success, function (err: Error | null): void { | ||
console.log("Done"); | ||
}); | ||
|
||
asyncDone(failure, function (err: Error | null): void { | ||
console.log("Done"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import asyncDone from "async-done"; | ||
import { Observable } from "rxjs/Observable"; | ||
import 'rxjs/add/observable/empty'; | ||
import 'rxjs/add/observable/of'; | ||
|
||
function success(): Observable<undefined> { | ||
return Observable.empty(); | ||
} | ||
|
||
function successValue(): Observable<number> { | ||
return Observable.of(42); | ||
} | ||
|
||
function failure(): Observable<number> { | ||
return Observable.throw(new Error("Observable error")); | ||
} | ||
|
||
// `success` callback | ||
asyncDone(success, function (err: Error | null): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// The following code fails to compile as expected (`undefined` is not assignable to `number`): | ||
// asyncDone(success, function (err: Error | null, result: number): void { | ||
// console.log("Done"); | ||
// }); | ||
|
||
// `successValue` and stricter callback | ||
asyncDone(successValue, function (err: Error | null, result?: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// `successValue` and unsound callback | ||
asyncDone(successValue, function (err: Error | null, result: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// `failure` and stricter callback | ||
asyncDone(failure, function (err: Error | null, result?: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// `failure` and unsound callback | ||
asyncDone(failure, function (err: Error | null, result: number): void { | ||
console.log("Done"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import asyncDone from "async-done"; | ||
|
||
function success(): Promise<number> { | ||
return Promise.resolve(2); | ||
} | ||
|
||
function failure(): Promise<number> { | ||
return Promise.reject(new Error("Promise Error")); | ||
} | ||
|
||
// `successValue` and stricter callback | ||
asyncDone(success, function (err: Error | null, result?: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// The following code fails to compile as expected: | ||
// asyncDone(success, function (err: Error | null, result?: string): void { | ||
// console.log("Done"); | ||
// }); | ||
|
||
// `successValue` and unsound callback | ||
asyncDone(success, function (err: Error | null, result: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// `failure` and stricter callback | ||
asyncDone(failure, function (err: Error | null, result?: number): void { | ||
console.log("Done"); | ||
}); | ||
|
||
// `failure` and unsound callback | ||
asyncDone(failure, function (err: Error | null, result: number): void { | ||
console.log("Done"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import asyncDone from "async-done"; | ||
import { Readable, Stream } from "stream"; | ||
|
||
function streamSuccess(): Stream { | ||
return new Stream(); | ||
} | ||
|
||
function streamFail(): Stream { | ||
return new Stream(); | ||
} | ||
|
||
asyncDone(streamSuccess, function (err: Error | null): void { | ||
console.log("Done"); | ||
}); | ||
|
||
asyncDone(streamFail, function (err: Error | null): void { | ||
console.log("Done"); | ||
}); |
Oops, something went wrong.