diff --git a/index.d.ts b/index.d.ts index 81e5ea58..f90ba362 100644 --- a/index.d.ts +++ b/index.d.ts @@ -21,6 +21,10 @@ export interface Nodeback { (err: E | null, value?: R): void } +export type ConcurrentRejected = ConcurrentFutureInstance; + +export type ConcurrentResolved = ConcurrentFutureInstance; + export interface ConcurrentFutureInstance { sequential: FutureInstance 'fantasy-land/ap'(this: ConcurrentFutureInstance B>, right: ConcurrentFutureInstance): ConcurrentFutureInstance @@ -28,6 +32,10 @@ export interface ConcurrentFutureInstance { 'fantasy-land/alt'(right: ConcurrentFutureInstance): ConcurrentFutureInstance } +export type Rejected = FutureInstance; + +export type Resolved = FutureInstance; + export interface FutureInstance { /** The Future constructor */ @@ -51,22 +59,46 @@ export interface FutureInstance { } /** Creates a Future which resolves after the given duration with the given value. See https://github.com/fluture-js/Fluture#after */ -export function after(duration: number): (value: R) => FutureInstance +export function after(duration: number): (value: R) => Resolved /** Logical and for Futures. See https://github.com/fluture-js/Fluture#and */ -export function and(left: FutureInstance): (right: FutureInstance) => FutureInstance +export function and(second: FutureInstance): { + (first: typeof never): typeof never + (first: Rejected): Rejected + (first: Resolved): FutureInstance + (first: FutureInstance): FutureInstance +} /** Logical or for Futures. See https://github.com/fluture-js/Fluture#alt */ -export function alt(left: FutureInstance): (right: FutureInstance) => FutureInstance +export function alt(second: FutureInstance): { + (first: typeof never): typeof never + (first: Rejected): FutureInstance + (first: Resolved): Resolved + (first: FutureInstance): FutureInstance +} /** Race two ConcurrentFutures. See https://github.com/fluture-js/Fluture#alt */ -export function alt(left: ConcurrentFutureInstance): (right: ConcurrentFutureInstance) => ConcurrentFutureInstance +export function alt(second: ConcurrentFutureInstance): (first: ConcurrentFutureInstance) => ConcurrentFutureInstance /** Apply the function in the right Future to the value in the left Future. See https://github.com/fluture-js/Fluture#ap */ -export function ap(value: FutureInstance): (apply: FutureInstance RB>) => FutureInstance +export function ap(left: Rejected): { + (right: typeof never): typeof never + (right: Rejected): Rejected + (right: Resolved<(value: any) => any>): Rejected + (right: FutureInstance any>): Rejected +} + +/** Apply the function in the right Future to the value in the left Future. See https://github.com/fluture-js/Fluture#ap */ +export function ap(left: FutureInstance): { + (right: typeof never): typeof never + (right: Rejected): Rejected + (right: Resolved<(value: RA) => RB>): FutureInstance + (right: FutureInstance RB>): FutureInstance +} /** Apply the function in the right ConcurrentFuture to the value in the left ConcurrentFuture. See https://github.com/fluture-js/Fluture#ap */ -export function ap(value: ConcurrentFutureInstance): (apply: ConcurrentFutureInstance RB>) => ConcurrentFutureInstance +export function ap(value: ConcurrentRejected): (apply: ConcurrentFutureInstance any>) => ConcurrentRejected +export function ap(value: ConcurrentFutureInstance): (apply: ConcurrentFutureInstance RB>) => ConcurrentFutureInstance /** Apply the function in the right Future to the value in the left Future in parallel. See https://github.com/fluture-js/Fluture#pap */ export function pap(value: FutureInstance): (apply: FutureInstance RB>) => FutureInstance @@ -78,7 +110,12 @@ export function attempt(fn: () => R): FutureInstance export function attemptP(fn: () => Promise): FutureInstance /** Create a Future using the inner value of the given Future. See https://github.com/fluture-js/Fluture#bichain */ -export function bichain(lmapper: (reason: LA) => FutureInstance): (rmapper: (value: RA) => FutureInstance) => (source: FutureInstance) => FutureInstance +export function bichain(onReject: (reason: LA) => FutureInstance): (onResolve: (value: RA) => FutureInstance) => { + (source: typeof never): typeof never + (source: Rejected): FutureInstance + (source: Resolved): FutureInstance + (source: FutureInstance): FutureInstance +} /** Map over both branches of the given Bifunctor at once. See https://github.com/fluture-js/Fluture#bimap */ export function bimap(lmapper: (reason: LA) => LB): (rmapper: (value: RA) => RB) => (source: FutureInstance) => FutureInstance @@ -111,7 +148,7 @@ export function extractLeft(source: FutureInstance): Array export function extractRight(source: FutureInstance): Array /** Coalesce both branches into the resolution branch. See https://github.com/fluture-js/Fluture#coalesce */ -export function coalesce(lmapper: (left: LA) => R): (rmapper: (right: RA) => R) => (source: FutureInstance) => FutureInstance +export function coalesce(lmapper: (left: LA) => R): (rmapper: (right: RA) => R) => (source: FutureInstance) => Resolved /** Fork the given Future into the given continuations. See https://github.com/fluture-js/Fluture#fork */ export function fork(reject: RejectFunction): (resolve: ResolveFunction) => (source: FutureInstance) => Cancel @@ -146,13 +183,13 @@ export function map(mapper: (value: RA) => RB): (mapper: (reason: LA) => LB): (source: FutureInstance) => FutureInstance /** A Future that never settles. See https://github.com/fluture-js/Fluture#never */ -export var never: FutureInstance +export var never: Resolved /** Create a Future using a provided Node-style callback. See https://github.com/fluture-js/Fluture#node */ export function node(fn: (done: Nodeback) => void): FutureInstance /** Create a Future with the given resolution value. See https://github.com/fluture-js/Fluture#of */ -export function resolve(value: R): FutureInstance +export function resolve(value: R): Resolved /** Run an Array of Futures in parallel, under the given concurrency limit. See https://github.com/fluture-js/Fluture#parallel */ export function parallel(concurrency: number): (futures: Array>) => FutureInstance> @@ -164,10 +201,10 @@ export function promise(source: FutureInstance): Promise export function race(left: FutureInstance): (right: FutureInstance) => FutureInstance /** Create a Future with the given rejection reason. See https://github.com/fluture-js/Fluture#reject */ -export function reject(reason: L): FutureInstance +export function reject(reason: L): Rejected /** Creates a Future which rejects after the given duration with the given reason. See https://github.com/fluture-js/Fluture#rejectafter */ -export function rejectAfter(duration: number): (reason: L) => FutureInstance +export function rejectAfter(duration: number): (reason: L) => Rejected /** Convert a ConcurrentFuture to a regular Future. See https://github.com/fluture-js/Fluture#concurrentfuture */ export function seq(source: ConcurrentFutureInstance): FutureInstance @@ -176,7 +213,7 @@ export function seq(source: ConcurrentFutureInstance): FutureInstanc export function swap(source: FutureInstance): FutureInstance /** Fork the Future into the given continuation. See https://github.com/fluture-js/Fluture#value */ -export function value(resolve: ResolveFunction): (source: FutureInstance) => Cancel +export function value(resolve: ResolveFunction): (source: Resolved) => Cancel /** Enable or disable debug mode. See https://github.com/fluture-js/Fluture#debugmode */ export function debugMode(debug: boolean): void; diff --git a/package.json b/package.json index ca9e0a92..8ef49aa7 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "test:build": "npm run build && oletus test/build/*.js", "coverage:upload": "c8 report --reporter=text-lcov > coverage.lcov && codecov", "coverage:report": "c8 report --reporter=html", - "test:types": "tsc index.d.ts" + "test:types": "tsd" }, "author": "Aldwin Vlasblom (https://github.com/Avaq)", "homepage": "https://github.com/fluture-js/Fluture", @@ -83,7 +83,11 @@ "sanctuary-benchmark": "^1.0.0", "sanctuary-either": "^2.0.0", "sanctuary-type-classes": "^12.0.0", + "tsd": "^0.11.0", "typescript": "^3.6.4", "xyz": "^4.0.0" + }, + "tsd": { + "directory": "test/types" } } diff --git a/test/types/after.test-d.ts b/test/types/after.test-d.ts new file mode 100644 index 00000000..5af5c1ee --- /dev/null +++ b/test/types/after.test-d.ts @@ -0,0 +1,6 @@ +import {expectType} from 'tsd'; + +import * as fl from '../../index.js'; + +expectType> (fl.after (1) (42)); +expectType> (fl.after (1) ('a')); diff --git a/test/types/alt.test-d.ts b/test/types/alt.test-d.ts new file mode 100644 index 00000000..1ab2de19 --- /dev/null +++ b/test/types/alt.test-d.ts @@ -0,0 +1,35 @@ +import {expectType} from 'tsd'; + +import * as fl from '../../index.js'; + +const fsn: fl.FutureInstance = fl.resolve (42); +const fns: fl.FutureInstance = fl.resolve ('a'); + +expectType> (fl.alt (fl.never) (fl.never)); +expectType> (fl.alt (fl.reject ('a')) (fl.never)); +expectType> (fl.alt (fl.resolve ('a')) (fl.never)); +expectType> (fl.alt (fl.never) (fl.reject ('a'))); +expectType> (fl.alt (fl.never) (fl.resolve ('a'))); +expectType> (fl.alt (fl.reject ('a')) (fl.resolve (42))); +expectType> (fl.alt (fl.resolve (42)) (fl.reject ('a'))); +expectType> (fl.alt (fl.resolve (42)) (fl.resolve (42))); +expectType> (fl.alt (fl.reject (42)) (fl.reject (42))); +expectType> (fl.alt (fl.resolve ('a')) (fl.resolve (42))); +expectType> (fl.alt (fl.reject ('a')) (fl.reject (42))); +expectType> (fl.alt (fsn) (fns)); + +const csn: fl.ConcurrentFutureInstance = fl.Par (fl.resolve (42)); +const cns: fl.ConcurrentFutureInstance = fl.Par (fl.resolve ('a')); + +expectType> (fl.alt (fl.Par (fl.never)) (fl.Par (fl.never))); +expectType> (fl.alt (fl.Par (fl.reject ('a'))) (fl.Par (fl.never))); +expectType> (fl.alt (fl.Par (fl.resolve ('a'))) (fl.Par (fl.never))); +expectType> (fl.alt (fl.Par (fl.never)) (fl.Par (fl.reject ('a')))); +expectType> (fl.alt (fl.Par (fl.never)) (fl.Par (fl.resolve ('a')))); +expectType> (fl.alt (fl.Par (fl.reject ('a'))) (fl.Par (fl.resolve (42)))); +expectType> (fl.alt (fl.Par (fl.resolve (42))) (fl.Par (fl.reject ('a')))); +expectType> (fl.alt (fl.Par (fl.resolve (42))) (fl.Par (fl.resolve (42)))); +expectType> (fl.alt (fl.Par (fl.reject (42))) (fl.Par (fl.reject (42)))); +expectType> (fl.alt (fl.Par (fl.resolve ('a'))) (fl.Par (fl.resolve (42)))); +expectType> (fl.alt (fl.Par (fl.reject ('a'))) (fl.Par (fl.reject (42)))); +expectType> (fl.alt (csn) (cns)); diff --git a/test/types/and.test-d.ts b/test/types/and.test-d.ts new file mode 100644 index 00000000..0b05de6e --- /dev/null +++ b/test/types/and.test-d.ts @@ -0,0 +1,18 @@ +import {expectType} from 'tsd'; + +import * as fl from '../../index.js'; + +const fsn: fl.FutureInstance = fl.resolve (42); +const fns: fl.FutureInstance = fl.resolve ('a'); + +expectType> (fl.and (fl.never) (fl.never)); +expectType> (fl.and (fl.never) (fl.reject ('a'))); +expectType> (fl.and (fl.never) (fl.resolve ('a'))); +expectType> (fl.and (fl.reject ('a')) (fl.never)); +expectType> (fl.and (fl.resolve ('a')) (fl.never)); +expectType> (fl.and (fl.resolve (42)) (fl.reject ('a'))); +expectType> (fl.and (fl.reject ('a')) (fl.resolve (42))); +expectType> (fl.and (fl.resolve (42)) (fl.resolve (42))); +expectType> (fl.and (fl.reject (42)) (fl.reject (42))); +expectType> (fl.and (fsn) (fsn)); +expectType> (fl.and (fsn) (fns)); diff --git a/test/types/ap.test-d.ts b/test/types/ap.test-d.ts new file mode 100644 index 00000000..36b691d3 --- /dev/null +++ b/test/types/ap.test-d.ts @@ -0,0 +1,55 @@ +import {expectType, expectError} from 'tsd'; + +import * as fl from '../../index.js'; + +const mx = fl.resolve (42); +const mf = fl.resolve ((x: number) => x + 1); + +const msx: fl.FutureInstance = fl.resolve (42); +const mnx: fl.FutureInstance = fl.resolve (42); +const msf: fl.FutureInstance number> = fl.resolve ((x: number) => x + 1); + +expectType> (fl.ap (mx) (mf)); +expectType> (fl.ap (msx) (msf)); +expectType> (fl.ap (mnx) (msf)); + +expectType> (fl.ap (fl.never) (fl.never)); +expectType> (fl.ap (mx) (fl.never)); +expectType> (fl.ap (fl.never) (mf)); + +expectType> (fl.ap (fl.reject ('a')) (fl.never)); +expectType> (fl.ap (fl.never) (fl.reject ('a'))); + +expectType> (fl.ap (fl.reject ('a')) (mf)); +expectType> (fl.ap (mx) (fl.reject ('a'))); +expectType> (fl.ap (fl.reject (42)) (fl.reject (42))); +expectType> (fl.ap (fl.reject ('a')) (fl.reject (42))); + +expectError (fl.ap (mx) (mx)); +expectError (fl.ap (mf) (mf)); + +const cmx = fl.Par (fl.resolve (42)); +const cmf = fl.Par (fl.resolve ((x: number) => x + 1)); + +const cmsx: fl.ConcurrentFutureInstance = fl.Par (fl.resolve (42)); +const cmnx: fl.ConcurrentFutureInstance = fl.Par (fl.resolve (42)); +const cmsf: fl.ConcurrentFutureInstance number> = fl.Par (fl.resolve ((x: number) => x + 1)); + +expectType> (fl.ap (cmx) (cmf)); +expectType> (fl.ap (cmsx) (cmsf)); +expectType> (fl.ap (cmnx) (cmsf)); + +expectType> (fl.ap (fl.Par (fl.never)) (fl.Par (fl.never))); +expectType> (fl.ap (cmx) (fl.Par (fl.never))); +expectType> (fl.ap (fl.Par (fl.never)) (cmf)); + +expectType> (fl.ap (fl.Par (fl.reject ('a'))) (fl.Par (fl.never))); +expectType> (fl.ap (fl.Par (fl.never)) (fl.Par (fl.reject ('a')))); + +expectType> (fl.ap (fl.Par (fl.reject ('a'))) (cmf)); +expectType> (fl.ap (cmx) (fl.Par (fl.reject ('a')))); +expectType> (fl.ap (fl.Par (fl.reject (42))) (fl.Par (fl.reject (42)))); +expectType> (fl.ap (fl.Par (fl.reject ('a'))) (fl.Par (fl.reject (42)))); + +expectError (fl.ap (cmx) (cmx)); +expectError (fl.ap (cmf) (cmf)); diff --git a/test/types/attempt-p.test-d.ts b/test/types/attempt-p.test-d.ts new file mode 100644 index 00000000..26709b93 --- /dev/null +++ b/test/types/attempt-p.test-d.ts @@ -0,0 +1,6 @@ +import {expectType} from 'tsd'; + +import * as fl from '../../index.js'; + +expectType> (fl.attemptP (() => Promise.resolve (42))); +expectType> (fl.attemptP (() => Promise.reject ('a'))); diff --git a/test/types/attempt.test-d.ts b/test/types/attempt.test-d.ts new file mode 100644 index 00000000..f5dec79e --- /dev/null +++ b/test/types/attempt.test-d.ts @@ -0,0 +1,6 @@ +import {expectType} from 'tsd'; + +import * as fl from '../../index.js'; + +expectType> (fl.attempt (() => 42)); +expectType> (fl.attempt (() => { throw new Error })); diff --git a/test/types/bichain.test-d.ts b/test/types/bichain.test-d.ts new file mode 100644 index 00000000..58816ef9 --- /dev/null +++ b/test/types/bichain.test-d.ts @@ -0,0 +1,31 @@ +import {expectType, expectError} from 'tsd'; + +import * as fl from '../../index.js'; + +const frej = (x: string) => fl.reject (x + '!'); +const fres = (x: string) => fl.resolve (x + '!'); +const grej = (y: number) => fl.reject (y + 1); +const gres = (y: number) => fl.resolve (y + 1); + +const msx = fl.reject ('a'); +const mxn = fl.resolve (42); +const msn: fl.FutureInstance = fl.resolve (42); + +expectType> (fl.bichain (frej) (grej) (msx)); +expectType> (fl.bichain (fres) (gres) (msx)); + +expectType> (fl.bichain (frej) (grej) (mxn)); +expectType> (fl.bichain (fres) (gres) (mxn)); + +expectType> (fl.bichain (frej) (grej) (msn)); +expectType> (fl.bichain (fres) (gres) (msn)); +expectType> (fl.bichain (fres) (grej) (msn)); +expectType> (fl.bichain (frej) (gres) (msn)); + +expectType> (fl.bichain (frej) (grej) (fl.never)); +expectType> (fl.bichain (fres) (gres) (fl.never)); + +expectError (fl.bichain (frej) (grej) (fl.reject (42))); +expectError (fl.bichain (frej) (grej) (fl.resolve ('a'))); +expectError (fl.bichain (fres) (gres) (fl.reject (42))); +expectError (fl.bichain (fres) (gres) (fl.resolve ('a')));