Improve R.evolve typings

This commit is contained in:
Marcin Biernat
2018-05-11 18:13:42 +02:00
parent 8cd6bba3b3
commit 8ca3d37a56
2 changed files with 73 additions and 17 deletions
+25 -5
View File
@@ -20,6 +20,7 @@
// Keagan McClelland <https://github.com/CaptJakk>
// Tomas Szabo <https://github.com/deftomat>
// Bonggyun Lee <https://github.com/deptno>
// Marcin Biernat <https://github.com/biern>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
@@ -82,9 +83,28 @@ declare namespace R {
(obj: Dictionary<T>): Dictionary<T>;
}
type Evolver<T> =
| ((x: T) => T)
| { [K in keyof T]?: Evolver<T[K]> };
type Evolve<O extends Evolvable<E>, E extends Evolver> = {
[P in keyof O]: P extends keyof E ? EvolveValue<O[P], E[P]> : O[P];
};
type EvolveValue<V, E> =
E extends (value: V) => any ? ReturnType<E> :
E extends Evolver ? EvolveNestedValue<V, E> :
never;
type EvolveNestedValue<V, E extends Evolver> =
V extends object ? (V extends Evolvable<E> ? Evolve<V, E> : never) : never;
interface Evolver {
[key: string]: ((value: any) => any) | Evolver;
}
// Represents all objects evolvable with Evolver E
type Evolvable<E extends Evolver> = {
[P in keyof E]?: E[P] extends (value: infer V) => any ? V :
E[P] extends Evolver ? Evolvable<E[P]> :
never
};
// @see https://gist.github.com/donnut/fd56232da58d25ceecf1, comment by @albrow
interface CurriedTypeGuard2<T1, T2, R extends T2> {
@@ -604,8 +624,8 @@ declare namespace R {
/**
* Creates a new object by evolving a shallow copy of object, according to the transformation functions.
*/
evolve<V>(transformations: Evolver<V>, obj: V): V;
evolve<V>(transformations: Evolver<V>): <W extends V>(obj: W) => W;
evolve<E extends Evolver, V extends Evolvable<E>>(transformations: E, obj: V): Evolve<V, E>;
evolve<E extends Evolver>(transformations: E): <V extends Evolvable<E>>(obj: V) => Evolve<V, E>;
/*
* A function that always returns false. Any passed in parameters are ignored.
+48 -12
View File
@@ -1307,19 +1307,55 @@ type Pair = KeyValuePair<string, number>;
};
() => {
const a1 = R.evolve({elapsed: R.add(1), remaining: R.add(-1)}, {name: "Tomato", elapsed: 100, remaining: 1400});
const a2 = R.evolve({elapsed: R.add(1), remaining: R.add(-1)})({name: "Tomato", elapsed: 100, remaining: 1400});
};
// No type transformation
() => {
// const tomato = {firstName: 'Tomato ', data: {elapsed: 100, remaining: 1400}, id:123};
// const transformations = {
// firstName: R.trim,
// lastName: R.trim, // Will not get invoked.
// data: {elapsed: R.add(1), remaining: R.add(-1)}
// };
// const a = R.evolve(transformations, tomato); // => {firstName: 'Tomato', data: {elapsed: 101, remaining: 1399}, id:123}
// const b = R.evolve(transformations)(tomato); // => {firstName: 'Tomato', data: {elapsed: 101, remaining: 1399}, id:123}
const a1 = R.evolve({ elapsed: R.add(1), remaining: R.add(-1) }, { name: "Tomato", elapsed: 100, remaining: 1400 });
const a1Test: { elapsed: number, remaining: number, name: string } = a1;
const a2 = R.evolve({ elapsed: R.add(1), remaining: R.add(-1) })({ name: "Tomato", elapsed: 100, remaining: 1400 });
const a2Test: { elapsed: number, remaining: number, name: string } = a2;
// Object doesn't have all evolver keys
const a3 = R.evolve({ age: R.add(1), name: R.trim }, { name: "Potato", elapsed: 100 });
const a3Test: { name: string, elapsed: number } = a3;
// Flat transformation
const ex0 = R.evolve({ a: parseInt }, { a: '10', b: 1 });
const ex0Test: { a: number, b: number } = ex0;
// Nested transformation:
const ex1 = R.evolve(
{ a: { b: R.toString, d: { e: R.toString } } },
{ a: { b: 1, c: null, d: { e: 2 } } },
);
const ex1Test: { a: { b: string, c: null, d: { e: string } } } = ex1;
// Mapping a nested object with a single function
const ex2 = R.evolve(
{ a: (obj: { foo: string }) => ({ bar: 1, baz: 2 }) },
{ a: { foo: 'a', skipped: 3 }, b: null },
);
const ex2Test: { a: { bar: number, baz: number }, b: null } = ex2;
// Nested curried:
const ex3 = R.evolve(
{ a: { b: R.toString, d: { e: R.toString } } },
)(
{ a: { b: 1, c: null, d: { e: 2 } } }
);
const ex3Test: { a: { b: string, c: null, d: { e: string } } } = ex3;
};
() => {