Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@
"types/**/*.ts",
"test/**/*.ts"
],
"globals": {
// a TypeScript lib type with no runtime binding, so `no-undef` cannot see it
"Generator": "readonly"
},
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
Expand Down
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

1. Run `npm install` to install needed local dependencies.

1. This package has no runtime dependencies, and shouldn't gain one. The type-level utilities
it needs (`Curry`, `Assign`, `Narrow`, ...) live in `types/util/_internal.d.ts`. A util file
whose name starts with `_` is internal: `npm run build` imports from it and ships it, but
does not re-export it, so adding to it does not widen the public API.

1. Run `npm run build` first, then `npm run test` and `npm run lint` and address any errors. Preferably, fix commits in place
using `git rebase` or `git commit --amend` to make the changes easier to
review and to keep the history tidy.
Expand Down
13 changes: 0 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@
"tsd": {
"directory": "test/"
},
"dependencies": {
"ts-toolbelt": "^9.6.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
Expand Down
7 changes: 5 additions & 2 deletions scripts/buildScripts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ const genExport = (x) => {

const genExports = (types) => types.map(genExport).join('\n\n');

// A util file whose name starts with `_` is internal: index.d.ts imports from it and it ships
// alongside the other declarations, but it is not re-exported as part of the public API.
const isInternal = (utilPath) => basename(utilPath).startsWith('_');

export const write = (utilDir, outDir, types) => {
const utilPaths = readdirSync(utilDir).map(p => join(utilDir, p));

Expand All @@ -151,12 +155,11 @@ export const write = (utilDir, outDir, types) => {
const exportsCode = genExports(types);

const otherExports = [
...utilPaths.map(p => `export * from './${basename(p, '.d.ts')}';`),
...utilPaths.filter(p => !isInternal(p)).map(p => `export * from './${basename(p, '.d.ts')}';`),
'export as namespace R;'
].join('\n');

const code = [
'import * as _ from \'ts-toolbelt\';',
...importsCode,
'',
exportsCode,
Expand Down
18 changes: 18 additions & 0 deletions test/binary.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expectType } from 'tsd';

import { binary } from '../es';

const f = (a: number, b: string, c: boolean) => 1;

expectType<number>(binary(f)(1, 'x'));

const rest = (...args: string[]) => 1;

// the rest element supplies both parameters, and supplies them exactly - not as `string |
// undefined`, the way running off the end of a fixed-length list would
expectType<(arg_0: string, arg_1: string) => number>(binary(rest));

const one = (a: number) => 1;

// a function shorter than the arity is padded out, not truncated
expectType<number>(binary(one)(1, undefined));
17 changes: 17 additions & 0 deletions test/curry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expectType } from 'tsd';

import { curry, __ } from '../es';

const f3 = (a: number, b: string, c: boolean) => 'r';

// every way of splitting the arguments lands on the same result
expectType<string>(curry(f3)(1, 'a', true));
expectType<string>(curry(f3)(1)('a')(true));
expectType<string>(curry(f3)(1, 'a')(true));
expectType<string>(curry(f3)(1)('a', true));

// `__` is accepted in any position, and holds the parameter under it open for a later call
expectType<string>(curry(f3)(__, 'a')(1, true));
expectType<string>(curry(f3)(__, __, true)(1, 'a'));
expectType<string>(curry(f3)(1, __, true)('a'));
expectType<string>(curry(f3)(__, 'a', true)(1));
15 changes: 15 additions & 0 deletions test/curryN.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expectType } from 'tsd';

import { curryN } from '../es';

const optional = (a: number, b?: string) => true;

// an optional parameter is still one of the N parameters being curried
expectType<boolean>(curryN(2, optional)(1)('x'));
expectType<boolean>(curryN(2, optional)(1, 'x'));

const rest = (a: number, ...others: string[]) => true;

// N reaches into the rest element rather than stopping at the last fixed parameter
expectType<boolean>(curryN(3, rest)(1)('x')('y'));
expectType<boolean>(curryN(3, rest)(1, 'x', 'y'));
10 changes: 10 additions & 0 deletions test/flatten.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expectType } from 'tsd';

import { flatten } from '../es';

// flattens all the way down, not one level
expectType<[1, 2, 3, 4]>(flatten([1, [2, [3, [4]]]] as [1, [2, [3, [4]]]]));
expectType<number[]>(flatten([[[1]]] as number[][][]));

// a tuple holding a plain array still flattens through it
expectType<(string | number)[]>(flatten([[[1]], 'x'] as [number[][], string]));
3 changes: 3 additions & 0 deletions test/flip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ import { flip, lt } from '../es';

expectType<boolean>(flip(lt)(2, 1));
expectType<boolean>(flip(lt)(2)(1));

// only the first two parameters swap; the rest keep their order
expectType<string>(flip((a: number, b: string, c: boolean) => 'r')('x', 1, true));
8 changes: 8 additions & 0 deletions test/mergeDeepLeft.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { expectType } from 'tsd';

import { mergeDeepLeft } from '../es';

expectType<{ a: { b: number; c: number } }>(mergeDeepLeft({ a: { b: 1 } }, { a: { c: 2 } }));

// the left side wins on conflict, at any depth
expectType<{ a: { b: number } }>(mergeDeepLeft({ a: { b: 1 } }, { a: { b: 'x' } }));
12 changes: 12 additions & 0 deletions test/mergeDeepRight.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { expectType } from 'tsd';

import { mergeDeepRight } from '../es';

// nested objects are merged, not replaced wholesale as mergeRight would
expectType<{ a: { b: number; c: number } }>(mergeDeepRight({ a: { b: 1 } }, { a: { c: 2 } }));

// the right side wins on conflict, at any depth
expectType<{ a: { b: string } }>(mergeDeepRight({ a: { b: 1 } }, { a: { b: 'x' } }));

// built-ins are values to be replaced, not objects to recurse into
expectType<{ a: { x: number } }>(mergeDeepRight({ a: new Date() }, { a: { x: 1 } }));
6 changes: 6 additions & 0 deletions test/mergeLeft.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ const foo2 = { foo: 2 };

expectType<{ foo: string; bar: string; }>(mergeLeft(foo, bar));
expectType<{ foo: string; }>(mergeLeft(foo, foo2));

declare const optional: { a?: number, z: 1 }, required: { a: string, y: 2 };

// an optional key on the left may or may not be there, so it unions with the right rather than
// shadowing it outright
expectType<{ a: string | number, z: 1, y: 2 }>(mergeLeft(optional, required));
17 changes: 17 additions & 0 deletions test/nAry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expectType } from 'tsd';

import { nAry } from '../es';

const f = (a: number, b: string, c: boolean) => 1;

expectType<number>(nAry(2, f)(1, 'x'));
expectType<number>(nAry(0, f)());

const rest = (a: number, ...others: string[]) => true;

expectType<boolean>(nAry(3, rest)(1, 'x', 'y'));

declare const n: number;

// a non-literal arity yields no parameters at all rather than diverging
expectType<number>(nAry(n, f)());
7 changes: 7 additions & 0 deletions test/unnest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { expectType } from 'tsd';

import { unnest } from '../es';

// removes exactly one level, unlike flatten
expectType<number[][]>(unnest([[[1]]] as number[][][]));
expectType<[1, 2, [3]]>(unnest([[1], [2, [3]]] as [[1], [2, [3]]]));
3 changes: 3 additions & 0 deletions test/zipObj.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ expectType<{string: string, number: number}>(pipe(
(a: [string, number]) => a,
zipObj(['string', 'number'])
)(['a', 42]));

// where a key repeats, the first occurrence wins
expectType<{a: 1}>(zipObj(['a', 'a'], [1, 2]));
10 changes: 5 additions & 5 deletions types/addIndex.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import * as _ from 'ts-toolbelt';
import { Curry } from './util/_internal';

// Special case for forEach
export function addIndex<T>(
fn: (f: (item: T) => void, list: readonly T[]) => T[],
): _.F.Curry<(a: (item: T, idx: number, list: T[]) => void, b: readonly T[]) => T[]>;
): Curry<(a: (item: T, idx: number, list: T[]) => void, b: readonly T[]) => T[]>;
// Special case for filter
export function addIndex<T>(
fn: (f: (item: T) => boolean, list: readonly T[]) => T[],
): _.F.Curry<(a: (item: T, idx: number, list: T[]) => boolean, b: readonly T[]) => T[]>;
): Curry<(a: (item: T, idx: number, list: T[]) => boolean, b: readonly T[]) => T[]>;
// Special case for map
export function addIndex<T, U>(
fn: (f: (item: T) => U, list: readonly T[]) => U[],
): _.F.Curry<(a: (item: T, idx: number, list: T[]) => U, b: readonly T[]) => U[]>;
): Curry<(a: (item: T, idx: number, list: T[]) => U, b: readonly T[]) => U[]>;
// Special case for reduce
export function addIndex<T, U>(
fn: (f: (acc: U, item: T) => U, aci: U, list: readonly T[]) => U,
): _.F.Curry<(a: (acc: U, item: T, idx: number, list: T[]) => U, b: U, c: readonly T[]) => U>;
): Curry<(a: (acc: U, item: T, idx: number, list: T[]) => U, b: U, c: readonly T[]) => U>;
4 changes: 2 additions & 2 deletions types/assocPath.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as _ from 'ts-toolbelt';
import { Curry } from './util/_internal';
import { Placeholder, Path } from './util/tools';

export function assocPath<T, U>(path: Path): _.F.Curry<(a: T, b: U) => U>;
export function assocPath<T, U>(path: Path): Curry<(a: T, b: U) => U>;
export function assocPath<T, U>(path: Path, val: T): (obj: U) => U;
export function assocPath<T, U>(__: Placeholder, val: T, obj: U): (path: Path) => U;
export function assocPath<T, U>(path: Path, __: Placeholder, obj: U): (val: T) => U;
Expand Down
5 changes: 2 additions & 3 deletions types/binary.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as _ from 'ts-toolbelt';
import { Take } from './util/tools';
import { TakeFirst } from './util/_internal';

export function binary<T extends (...arg: any) => any>(fn: T): (...arg: _.T.Take<Parameters<T>, 2>) => ReturnType<T>;
export function binary<T extends (...arg: any) => any>(fn: T): (...arg: TakeFirst<Parameters<T>, 2>) => ReturnType<T>;
4 changes: 2 additions & 2 deletions types/construct.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Curry } from './util/_internal';

import * as _ from 'ts-toolbelt';

export function construct<A extends any[], T>(
constructor: { new (...a: A): T } | ((...a: A) => T),
): _.F.Curry<(...a: A) => T>;
): Curry<(...a: A) => T>;
4 changes: 2 additions & 2 deletions types/constructN.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Curry } from './util/_internal';

import * as _ from 'ts-toolbelt';
import { mergeArrWithLeft, Tuple } from './util/tools';

export function constructN<A extends any[], T, N extends number>(
n: N,
constructor: { new (...a: A): T } | ((...a: A) => T),
): _.F.Curry<(...a: mergeArrWithLeft<Tuple<any, N>, A>) => T>;
): Curry<(...a: mergeArrWithLeft<Tuple<any, N>, A>) => T>;
6 changes: 3 additions & 3 deletions types/converge.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as _ from 'ts-toolbelt';
import { Curry } from './util/_internal';
import { Fn, IfFunctionsArgumentsDoNotOverlap, ReturnTypesOfFns, LargestArgumentsList } from './util/tools';

export function converge<
Expand All @@ -9,7 +9,7 @@ export function converge<
>(
converging: (...args: ReturnTypesOfFns<FunctionsList>) => TResult,
branches: FunctionsList,
): _.F.Curry<(...args: LargestArgumentsList<FunctionsList>) => TResult>;
): Curry<(...args: LargestArgumentsList<FunctionsList>) => TResult>;
export function converge<
CArgs extends ReadonlyArray<any>,
TResult,
Expand All @@ -23,4 +23,4 @@ export function converge<
>(
converging: (...args: CArgs) => TResult,
branches: FunctionsList,
): _.F.Curry<(...args: LargestArgumentsList<FunctionsList>) => TResult>;
): Curry<(...args: LargestArgumentsList<FunctionsList>) => TResult>;
4 changes: 2 additions & 2 deletions types/curry.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import * as _ from 'ts-toolbelt';
import { Curry } from './util/_internal';

export function curry<F extends (...args: any) => any>(f: F): _.F.Curry<F>;
export function curry<F extends (...args: any) => any>(f: F): Curry<F>;
7 changes: 3 additions & 4 deletions types/curryN.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as _ from 'ts-toolbelt';
import { Take } from './util/tools';
import { Curry, TakeFirst } from './util/_internal';

export function curryN<N extends number>(
length: N,
): <F extends (...args: any) => any>(
fn: F,
) => _.F.Curry<(...a: _.T.Take<Parameters<F>, N>) => ReturnType<F>>;
) => Curry<(...a: TakeFirst<Parameters<F>, N>) => ReturnType<F>>;
export function curryN<N extends number, F extends (...args: any) => any>(
length: N,
fn: F,
): _.F.Curry<(...a: _.T.Take<Parameters<F>, N>) => ReturnType<F>>;
): Curry<(...a: TakeFirst<Parameters<F>, N>) => ReturnType<F>>;
4 changes: 2 additions & 2 deletions types/flatten.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import * as _ from 'ts-toolbelt';
import { Flatten } from './util/_internal';

export function flatten<T extends readonly any[]>(list: T): _.T.Flatten<T>;
export function flatten<T extends readonly any[]>(list: T): Flatten<T>;
6 changes: 3 additions & 3 deletions types/flip.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as _ from 'ts-toolbelt';
import { Curry, Swap2 } from './util/_internal';

export function flip<T, U, TResult>(fn: (arg0: T, arg1: U) => TResult): {
(arg1: U): (arg0: T) => TResult;
(arg1: U, arg0: T): TResult;
};
export function flip<F extends (...args: any) => any, P extends _.F.Parameters<F>>(
export function flip<F extends (...args: any) => any, P extends Parameters<F>>(
fn: F,
): _.F.Curry<(...args: _.T.Merge<[P[1], P[0]], P>) => _.F.Return<F>>;
): Curry<(...args: Swap2<P>) => ReturnType<F>>;
4 changes: 2 additions & 2 deletions types/mergeAll.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as _ from 'ts-toolbelt';
import { Assign } from './util/_internal';

// for when passing in an object literal of different objects, eg `mergeAll([obj1: T1, obj2: T2, obj3: T3])`
// you get back essentially a cleaner version of `T1 & T2 & T3`
export function mergeAll<T extends object, Ts extends readonly object[]>(list: [T, ...Ts]): _.O.Assign<T, Ts>;
export function mergeAll<T extends object, Ts extends readonly object[]>(list: [T, ...Ts]): Assign<T, Ts>;
// for when passing in an `T[]` where all the objects are the same shape `mergeAll([obj1, obj2, obj3]: T[])
// this just returns T
export function mergeAll<T>(list: readonly T[]): T;
6 changes: 3 additions & 3 deletions types/mergeDeepLeft.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as _ from 'ts-toolbelt';
import { Assign } from './util/_internal';

export function mergeDeepLeft<L extends object>(l: L): <R extends object>(r: R) => _.O.Assign<R, [L], 'deep'>;
export function mergeDeepLeft<L extends object, R extends object>(l: L, r: R): _.O.Assign<R, [L], 'deep'>;
export function mergeDeepLeft<L extends object>(l: L): <R extends object>(r: R) => Assign<R, [L], 'deep'>;
export function mergeDeepLeft<L extends object, R extends object>(l: L, r: R): Assign<R, [L], 'deep'>;
6 changes: 3 additions & 3 deletions types/mergeDeepRight.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as _ from 'ts-toolbelt';
import { Assign } from './util/_internal';

export function mergeDeepRight<L extends object>(l: L): <R extends object>(r: R) => _.O.Assign<L, [R], 'deep'>;
export function mergeDeepRight<L extends object, R extends object>(l: L, r: R): _.O.Assign<L, [R], 'deep'>;
export function mergeDeepRight<L extends object>(l: L): <R extends object>(r: R) => Assign<L, [R], 'deep'>;
export function mergeDeepRight<L extends object, R extends object>(l: L, r: R): Assign<L, [R], 'deep'>;
6 changes: 3 additions & 3 deletions types/mergeLeft.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as _ from 'ts-toolbelt';
import { Assign } from './util/_internal';

// Note: ramda `mergeLeft` uses `Object.assign` in code, so we need to use `O.Assign` here, and not `O.Merge`
export function mergeLeft<L extends object>(l: L): <R extends object>(r: R) => _.O.Assign<R, [L], 'flat'>;
export function mergeLeft<L extends object, R extends object>(l: L, r: R): _.O.Assign<R, [L], 'flat'>;
export function mergeLeft<L extends object>(l: L): <R extends object>(r: R) => Assign<R, [L], 'flat'>;
export function mergeLeft<L extends object, R extends object>(l: L, r: R): Assign<R, [L], 'flat'>;
6 changes: 3 additions & 3 deletions types/mergeRight.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as _ from 'ts-toolbelt';
import { Assign } from './util/_internal';

// Note: ramda `mergeLeft` uses `Object.assign` in code, so we need to use `O.Assign` here, and not `O.Merge`
export function mergeRight<L extends object>(l: L): <R extends object>(r: R) => _.O.Assign<L, [R], 'flat'>;
export function mergeRight<L extends object, R extends object>(l: L, r: R): _.O.Assign<L, [R], 'flat'>;
export function mergeRight<L extends object>(l: L): <R extends object>(r: R) => Assign<L, [R], 'flat'>;
export function mergeRight<L extends object, R extends object>(l: L, r: R): Assign<L, [R], 'flat'>;
Loading
Loading