-
Notifications
You must be signed in to change notification settings - Fork 30.5k
Add types for gensync #60125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add types for gensync #60125
Changes from all commits
58c6058
0fcac07
92e2713
c63e411
485f7ca
adf933e
56852a5
bd6deec
14c82e5
9923e80
66b417e
a71a3f8
5e898a2
0e87685
0470bb9
78bd53e
04c93ac
c59efc6
a72eb8a
828b999
cbd3f0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| import gensync = require('gensync'); | ||
|
|
||
| // Fake node fs.readFile for testing. | ||
| declare function readFileCallback( | ||
| path: string, | ||
| encoding: 'utf8' | 'ascii', | ||
| cb: (err: Error, result: string) => void, | ||
| ): void; | ||
| declare function readFileSync(path: string, encoding: 'utf8' | 'ascii'): string; | ||
| declare function readFileAsync(path: string, encoding: 'utf8' | 'ascii'): Promise<string>; | ||
|
|
||
| // $ExpectType Gensync<[path: string, encoding: "utf8" | "ascii"], string, unknown> | ||
| const readFileFromSync = gensync({ | ||
| name: 'readFile', | ||
| arity: 2, | ||
| sync: readFileSync, | ||
| }); | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii") => string | ||
| readFileFromSync.sync; | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii") => Promise<string> | ||
| readFileFromSync.async; | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii", callback: (err: unknown, result: string) => void) => void | ||
| readFileFromSync.errback; | ||
|
|
||
| // $ExpectType Gensync<[path: string, encoding: "utf8" | "ascii"], string, unknown> | ||
| const readFileFromAsync = gensync({ | ||
| name: 'readFile', | ||
| sync: readFileSync, | ||
| async: readFileAsync, | ||
| }); | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii") => string | ||
| readFileFromAsync.sync; | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii") => Promise<string> | ||
| readFileFromAsync.async; | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii", callback: (err: unknown, result: string) => void) => void | ||
| readFileFromAsync.errback; | ||
|
|
||
| // $ExpectType Gensync<[path: string, encoding: "utf8" | "ascii"], string, Error> | ||
| const readFileFromErrback = gensync({ | ||
| name: 'readFile', | ||
| sync: readFileSync, | ||
| errback: readFileCallback, | ||
| }); | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii") => string | ||
| readFileFromErrback.sync; | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii") => Promise<string> | ||
| readFileFromErrback.async; | ||
|
|
||
| // $ExpectType (path: string, encoding: "utf8" | "ascii", callback: (err: Error, result: string) => void) => void | ||
| readFileFromErrback.errback; | ||
|
|
||
| // $ExpectType Gensync<[], void, unknown> | ||
| const noop = gensync(function* () { }); | ||
|
|
||
| // $ExpectType () => void | ||
| noop.sync; | ||
|
|
||
| // $ExpectType () => Promise<void> | ||
| noop.async; | ||
|
|
||
| // $ExpectType (callback: (err: unknown) => void) => void | ||
| noop.errback; | ||
|
|
||
| gensync({ | ||
| sync: () => { }, | ||
| errback: callback => { | ||
| callback(new Error()); | ||
| }, | ||
| }); | ||
|
|
||
| const addNumbers = gensync(function* (a: number, b?: number) { | ||
| return a + (b ?? 0); | ||
| }); | ||
|
|
||
| const pathJoin = gensync(function* (...args: string[]) { | ||
| return args.join('/'); | ||
| }); | ||
|
|
||
| const readContents = gensync(function* (p: string) { | ||
| const path = yield* pathJoin('folder', p); | ||
| const contents = yield* readFileFromSync(path, 'utf8'); | ||
| return contents; | ||
| }); | ||
|
|
||
| // $ExpectType string | ||
| readContents.sync('foo'); | ||
|
|
||
| async function readContentsAsync() { | ||
| // $ExpectType string | ||
| await readContents.async('foo'); | ||
| } | ||
|
|
||
| readContents.errback('foo', (err, result) => { | ||
| // $ExpectType unknown | ||
| err; | ||
| // $ExpectType string | ||
| result; | ||
| }); | ||
|
|
||
| const isAsync = gensync<[], boolean, null>({ | ||
| sync: () => false, | ||
| errback: cb => cb(null, true), | ||
| }); | ||
|
|
||
| isAsync.errback((err, condition) => { | ||
| // $ExpectType null | ||
| err; | ||
|
|
||
| // $ExpectType boolean | ||
| condition; | ||
| }); | ||
|
|
||
| gensync(function* () { | ||
| // $ExpectType [number, string] | ||
| yield* gensync.all([addNumbers(1, 2), readContents('foo')]); | ||
|
|
||
| // $ExpectType string | number | ||
| yield* gensync.race([addNumbers(1, 2), readContents('foo')]); | ||
| }); | ||
|
|
||
| gensync(function* () { | ||
| const gens = [addNumbers(1, 2), readContents('foo')]; | ||
|
|
||
| // $ExpectType (string | number)[] | ||
| yield* gensync.all(gens); | ||
|
|
||
| // $ExpectType string | number | ||
| yield* gensync.race(gens); | ||
| }); | ||
|
|
||
| declare const iterable: Iterable<gensync.Handler<number | boolean>>; | ||
|
|
||
| gensync(function* () { | ||
| // $ExpectType (number | boolean)[] | ||
| yield* gensync.all(iterable); | ||
|
|
||
| // $ExpectType number | boolean | ||
| yield* gensync.race(iterable); | ||
| }); | ||
|
|
||
| // gensync throws when both async and errback are provided. | ||
| gensync({ | ||
| name: 'readFile', | ||
| sync: readFileSync, | ||
| // @ts-expect-error | ||
| async: readFileAsync, | ||
| errback: readFileCallback, | ||
| }); | ||
|
|
||
| function* someOtherGenerator() { | ||
| yield 'this is not a gensync generator'; | ||
| return 1234; | ||
| } | ||
|
|
||
| // @ts-expect-error | ||
| gensync(function* () { | ||
| // This generator was not produced by gensync; error. | ||
| // It"d be better to have an error on the next line rather than above, | ||
| // but the generator type that's produced via the body appears to have | ||
| // higher precedence than the contextual type. | ||
| yield* someOtherGenerator(); | ||
| }); | ||
|
|
||
| // @ts-expect-error | ||
| gensync(() => { }); | ||
|
|
||
| // @ts-expect-error | ||
| gensync({}); | ||
|
|
||
| gensync(function* () { | ||
| // @ts-expect-error | ||
| yield* gensync.all(['not a generator']); | ||
|
|
||
| // @ts-expect-error | ||
| yield* gensync.all([someOtherGenerator()]); | ||
|
|
||
| // @ts-expect-error | ||
| yield* gensync.race(['not a generator']); | ||
|
|
||
| // @ts-expect-error | ||
| yield* gensync.race([someOtherGenerator()]); | ||
| }); | ||
|
|
||
| gensync({ | ||
| sync: () => { }, | ||
| errback: callback => { | ||
| // @ts-expect-error | ||
| callback(new Error(), 'some result'); | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| // Type definitions for gensync 1.0 | ||
| // Project: https://github.com/loganfsmyth/gensync | ||
| // Definitions by: Jake Bailey <https://github.com/jakebailey> | ||
| // Nicolò Ribaudo <https://github.com/nicolo-ribaudo> | ||
| // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped | ||
| // Minimum TypeScript Version: 4.0 | ||
|
|
||
| /** | ||
| * Returns a function that can be "awaited" (with `yield*`) in another `gensync` generator | ||
| * function, or executed via | ||
| * | ||
| * - `.sync(...args)` - Returns the computed value, or throws. | ||
| * - `.async(...args)` - Returns a promise for the computed value. | ||
| * - `.errback(...args, (err, result) => {})` - Calls the callback with the computed value, or error. | ||
| * @param generatorFnOrOptions A generator function, or options for an existing sync/async function | ||
| */ | ||
| declare function gensync<A extends unknown[], R, E = unknown>( | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I defaulted |
||
| generatorFnOrOptions: ((...args: A) => Generator<gensync.Handler, R>) | gensync.Options<A, R, E>, | ||
| ): gensync.Gensync<A, R, E>; | ||
|
|
||
| declare namespace gensync { | ||
| /** | ||
| * A generator produced by `gensync`, which can only "await" (with `yield*`) other | ||
| * generators produced by `gensync`. | ||
| */ | ||
| type Handler<R = unknown> = Generator<Handler, R>; | ||
|
|
||
| /** | ||
| * Given a `gensync` generator, produces the "awaited" type of that generator | ||
| * when "yield*"'d in another `gensync` generator. | ||
| */ | ||
| type Handled<T> = T extends Handler<infer U> ? U : never; | ||
|
|
||
| /** | ||
| * A callback function such that if the result is void, there is no result parameter. | ||
| */ | ||
| // tslint:disable-next-line void-return | ||
| type Callback<R, E = unknown> = [R] extends [void] ? (err: E) => void : (err: E, result: R) => void; | ||
|
|
||
| /** | ||
| * A function that can be "awaited" (with `yield*`) in another `gensync` generator, | ||
| * or executed via | ||
| * | ||
| * - `.sync(...args)` - Returns the computed value, or throws. | ||
| * - `.async(...args)` - Returns a promise for the computed value. | ||
| * - `.errback(...args, (err, result) => {})` - Calls the callback wit | ||
| */ | ||
| interface Gensync<A extends unknown[], R, E = unknown> { | ||
| (...args: A): Handler<R>; | ||
| sync(...args: A): R; | ||
| async(...args: A): Promise<R>; | ||
| errback(...args: [...args: A, callback: Callback<R, E>]): void; | ||
| } | ||
|
|
||
| interface SyncOptions<A extends unknown[], R> { | ||
| /** | ||
| * A function that will be called when `.sync()` is called on the `gensync()` | ||
| * result, or when the result is passed to `yield*` in another generator that | ||
| * is being run synchronously. | ||
| * | ||
| * Also called for `.async()` calls if no async handlers are provided. | ||
| */ | ||
| sync: (...args: A) => R; | ||
|
|
||
| /** | ||
| * A string name to apply to the returned function. If no value is provided, | ||
| * the name of `errback`/`async`/`sync` functions will be used, with any | ||
| * `Sync` or `Async` suffix stripped off. If the callback is simply named | ||
| * with ES6 inference (same name as the options property), the name is ignored. | ||
| */ | ||
| name?: string | undefined; | ||
|
|
||
| /** | ||
| * A number for the length to set on the returned function. If no value | ||
| * is provided, the length will be carried over from the `sync` function's | ||
| * `length` value. | ||
| */ | ||
| arity?: number | undefined; | ||
|
|
||
| // Mutually exclusive options. | ||
| async?: undefined; | ||
| errback?: undefined; | ||
| } | ||
|
|
||
| interface AsyncOptions<A extends unknown[], R> extends Omit<SyncOptions<A, R>, 'async'> { | ||
| /** | ||
| * A function that will be called when `.async()` or `.errback()` is called on | ||
| * the `gensync()` result, or when the result is passed to `yield*` in another | ||
| * generator that is being run asynchronously. | ||
| * | ||
| * Must not be specified with `errback`. | ||
| */ | ||
| async: (...args: A) => Promise<R>; | ||
| } | ||
|
|
||
| interface ErrbackOptions<A extends unknown[], R, E = unknown> extends Omit<SyncOptions<A, R>, 'errback'> { | ||
| /** | ||
| * A function that will be called when `.async()` or `.errback()` is called on | ||
| * the `gensync()` result, or when the result is passed to `yield*` in another | ||
| * generator that is being run asynchronously. | ||
| * | ||
| * This option allows for simpler compatibility with many existing Node APIs, | ||
| * and also avoids introducing the extra even loop turns that promises introduce | ||
| * to access the result value. | ||
| * | ||
| * Must not be specified with `async`. | ||
| */ | ||
| errback: (...args: [...A, Callback<R, E>]) => void; | ||
| } | ||
|
|
||
| type Options<A extends unknown[], R, E = unknown> = | ||
| | SyncOptions<A, R> | ||
| | AsyncOptions<A, R> | ||
| | ErrbackOptions<A, R, E>; | ||
|
|
||
| // "all" and "race"'s types are pretty much copied from Promise.all and Promise.race, | ||
| // replacing Awaited with Handled. | ||
|
|
||
| /** | ||
| * `Promise.all`-like combinator that works with an iterable of generator objects | ||
| * that could be passed to `yield*` within a gensync generator. | ||
| * @param args An array of gensync generators | ||
| * @returns A new gensync generator | ||
| */ | ||
| function all<T extends readonly Handler[] | []>(args: T): Handler<{ -readonly [P in keyof T]: Handled<T[P]> }>; | ||
|
|
||
| /** | ||
| * `Promise.all`-like combinator that works with an iterable of generator objects | ||
| * that could be passed to `yield*` within a gensync generator. | ||
| * @param args An iterable of gensync generators | ||
| * @returns A new gensync generator | ||
| */ | ||
| function all<T>(args: Iterable<Handler<T>>): Handler<T[]>; | ||
|
|
||
| /** | ||
| * `Promise.race`-like combinator that works with an iterable of generator objects | ||
| * that could be passed to `yield*` within a gensync generator. | ||
| * @param args An array of gensync generators | ||
| * @returns A new gensync generator | ||
| */ | ||
| function race<T extends readonly Handler[] | []>(args: T): Handler<Handled<T[number]>>; | ||
|
|
||
| /** | ||
| * `Promise.race`-like combinator that works with an iterable of generator objects | ||
| * that could be passed to `yield*` within a gensync generator. | ||
| * @param args An iterable of gensync generators | ||
| * @returns A new gensync generator | ||
| */ | ||
| function race<T>(args: Iterable<Handler<T>>): Handler<T>; | ||
| } | ||
|
|
||
| export = gensync; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "module": "commonjs", | ||
| "lib": ["es6"], | ||
| "target": "es6", | ||
| "noImplicitAny": true, | ||
| "noImplicitThis": true, | ||
| "strictFunctionTypes": true, | ||
| "strictNullChecks": true, | ||
| "baseUrl": "../", | ||
| "typeRoots": ["../"], | ||
| "types": [], | ||
| "noEmit": true, | ||
| "forceConsistentCasingInFileNames": true | ||
| }, | ||
| "files": ["index.d.ts", "gensync-tests.ts"] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "extends": "@definitelytyped/dtslint/dt.json", | ||
| "rules": { | ||
| "space-before-function-paren": false | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this one required? It can be a prettier nitpit setting, that can be disabled via prettier inline comment
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Required, no, but every single use of a generator (so, every single test) would need to have a prettier comment.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm somewhat inclined to go figure out if there's something in dtslint that can be changed to not complain about this particular code. But, then again, I would really like DT to enforce prettier-d code in the first place, so what can I do? 😄
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, really sorry to miss a point here. I"m pretty sure Prettier always space this:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All good. Prettier wants
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd really opt to add this change upstream, I had the same problem couple of times here. This will eventually incrementally increase the toil on developers, if not corrected. |
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.