From 0f510a09e0307fcf3aaee198c9d002dbfcf05f63 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Sun, 16 Jul 2023 18:47:34 +0200 Subject: [PATCH] Add another Core version with partial and pipe as separate helpers --- src/internals/core/Core2.ts | 99 ++++++---- src/internals/core/Core3.ts | 357 ++++++++++++++++++++++++++++++++++++ 2 files changed, 418 insertions(+), 38 deletions(-) create mode 100644 src/internals/core/Core3.ts diff --git a/src/internals/core/Core2.ts b/src/internals/core/Core2.ts index ab7dec5..800dd5b 100644 --- a/src/internals/core/Core2.ts +++ b/src/internals/core/Core2.ts @@ -2,6 +2,7 @@ import { _, unset } from "./Core"; import { ExcludePlaceholders, ExcludeUnset, MergeArgs } from "./impl/MergeArgs"; import * as NumberImpl from "../numbers/impl/numbers"; import * as StringImpl from "../strings/impl/strings"; +import { Equal, Expect } from "../helpers"; /** * Core @@ -14,16 +15,6 @@ export interface Fn { return: unknown; } -type Drop< - xs extends readonly unknown[], - n extends number, - dropped extends readonly unknown[] = [] -> = n extends dropped["length"] - ? xs - : xs extends readonly [infer first, ...infer tail] - ? Drop - : []; - type ExcludePlaceholdersFromInputTypes< inputTypes extends unknown[], partialArgs extends unknown[], @@ -68,7 +59,7 @@ export type Apply = (fn & { args: args; })["return"]; -type AnyAp = Ap; +type AnyAp = Ap; export type $< fn extends Fn, @@ -107,6 +98,7 @@ type ExpectNumber = [a]; // arguments are typed internally: interface TakeNumAndStr extends Fn<[number, string], boolean> { works: ExpectNumber>; // ✅ + // @ts-expect-error fails: ExpectNumber>; // ~~~~~~~~~~ ❌ return: true; @@ -120,44 +112,61 @@ interface Div extends Fn<[number, number], number> { * Full application */ -type x = $; // 5 +type t1 = $; // 5 // ^? -type y = $; -// ~~~ ❌ -type z = $; -// ~~~ ❌ +type test1 = Expect>; + +// @ts-expect-error +type err1 = $; +// ~~~ ❌ + +// @ts-expect-error +type err2 = $; +// ~~~ ❌ /** * Partial application in order */ type Div1 = $; -type Three = $; +type t2 = $; +// ^? +type test2 = Expect>; // ^? -type w = $<$, "2">; -// ~~~ ❌ + +// @ts-expect-error +type t3 = $<$, "2">; +// ~~~ ❌ /** * Partial application different order */ type DivBy2 = $; // ^? -type q = $; // 5 ✅ +type t4 = $; // 5 ✅ // ^? +type test4 = Expect>; -type r = $<$, 10, 5>; // ✅ +type t5 = $<$, 10, 5>; // ✅ // ^? +type test5 = Expect>; type TakeStr = $; // ^? Ap -type e = $; -// ~~ ❌ + +// @ts-expect-error +type t8 = $; +// ~~ ❌ type TakeNum = $; // ^?Ap -type s = $; // ✅ -type d = $; -// ~~~~ ❌ + +type t7 = $; // ✅ +type test7 = Expect>; + +// @ts-expect-error +type t8 = $; +// ~~~~ ❌ /** * Higher order @@ -169,8 +178,9 @@ interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { : never; } -type z2 = $, $, [2, 4, 6, 8, 10]>; +type t9 = $, $, [2, 4, 6, 8, 10]>; // ^? [1, 2, 3, 4, 5] +type test9 = Expect>; interface Add extends Fn<[number, number], number> { return: NumberImpl.Add, Arg1>; @@ -194,14 +204,21 @@ interface Reduce : never; } -type reduced1 = $, Add, 0, [2, 4, 6, 8, 10]>; +type t11 = $, Add, 0, [2, 4, 6, 8, 10]>; // ^? 30 -type reduced2 = $, Mul, 1, [2, 4, 6, 8, 10]>; +type test11 = Expect>; + +type t12 = $, Mul, 1, [2, 4, 6, 8, 10]>; // ^? 3840 -type reduced3 = $, Mul, 1, ["2", "4", "6", "8", "10"]>; -// ~~~~~~~~~~~~~~~~~~~~~~~~~~ ❌ -type reducedOops = $; -// ~~~~~~ ❌ +type test12 = Expect>; + +// @ts-expect-error +type t13 = $, Mul, 1, ["2", "4", "6", "8", "10"]>; +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ ❌ + +// @ts-expect-error +type t14 = $; +// ~~~~~~ ❌ interface NumToStringReducer extends Fn<[string, number], string> { return: `${Arg0}${Arg1}`; @@ -212,12 +229,14 @@ interface StringToNumReducer extends Fn<[number, string], number> { } // prettier-ignore -type reduced4 = $, StringToNumReducer, 1, ["a", "aa", "aaa", "aaaa", "aaaaa"]>; +type t15 = $, StringToNumReducer, 1, ["a", "aa", "aaa", "aaaa", "aaaaa"]>; // ^? 16 +type test15 = Expect>; +// @ts-expect-error // prettier-ignore -type reduced5 = $, NumToStringReducer, 1, ["a", "aa", "aaa", "aaaa", "aaaaa"]>; -// ~~~~~~~~~~~~~~~~~~ ❌ +type t16 = $, NumToStringReducer, 1, ["a", "aa", "aaa", "aaaa", "aaaaa"]>; +// ~~~~~~~~~~~~~~~~~~ ❌ interface ToString extends Fn<[number], string> { return: `${Arg0}`; @@ -237,15 +256,19 @@ interface ToArray extends Fn<[unknown], [unknown]> { type Times10 = $>; -type test1 = Times10<10>; +type t17 = Times10<10>; // ^? 110 +type test17 = Expect>; +// @ts-expect-error type WrongComposition1 = $>; // ~~~~~~~~~~~~~~ ❌ +// @ts-expect-error type WrongComposition2 = $>; // ~~~~~~~~~~~~~~ ❌ type Test = $; -type test2 = Test<"10">; +type t18 = Test<"10">; // ^? 110 +type test18 = Expect>; diff --git a/src/internals/core/Core3.ts b/src/internals/core/Core3.ts new file mode 100644 index 0000000..3c3d08d --- /dev/null +++ b/src/internals/core/Core3.ts @@ -0,0 +1,357 @@ +import { _, unset } from "./Core"; +import { ExcludePlaceholders, ExcludeUnset, MergeArgs } from "./impl/MergeArgs"; +import * as NumberImpl from "../numbers/impl/numbers"; +import * as StringImpl from "../strings/impl/strings"; +import { Equal, Expect } from "../helpers"; + +/** + * Core + */ + +export interface Fn { + inputTypes: input; + outputType: output; + args: unknown; + return: unknown; +} + +export type Apply = (fn & { + args: args; +})["return"]; + +type ExcludePlaceholdersFromInputTypes< + inputTypes extends unknown[], + partialArgs extends unknown[], + result extends unknown[] = [] +> = [inputTypes, partialArgs] extends [ + [infer fInput, ...infer rInput], + [infer fPartial, ...infer rPartial] +] + ? ExcludePlaceholdersFromInputTypes< + rInput, + rPartial, + fPartial extends _ ? [...result, fInput] : result + > + : [...result, ...inputTypes]; + +interface Applied + extends Fn { + name: "$.partial"; + + argsArray: Extract; + allArgs: [...partialArgs, ...this["argsArray"]]; + + inputTypes: ExcludePlaceholdersFromInputTypes; + + outputType: fn["outputType"]; + + return: Apply>; +} + +namespace Tuple { + export type Last = xs extends [...any, infer last] ? last : never; +} + +type ApplyLeftToRight = fns extends [ + infer fn extends Fn, + ...infer restFns +] + ? ApplyLeftToRight<[Apply], restFns> + : x[0]; + +interface Piped extends Fn { + name: "$.pipe"; + + inputTypes: fns[0]["inputTypes"]; + outputType: Extract, Fn>["outputType"]; + + return: ApplyLeftToRight, fns>; +} + +namespace $ { + export type partial< + fn extends Fn, + arg0 extends fn["inputTypes"][0] | _ = unset, + arg1 extends fn["inputTypes"][1] | _ = unset, + arg2 extends fn["inputTypes"][2] | _ = unset, + arg3 extends fn["inputTypes"][3] | _ = unset + > = Applied>; + + type getOutputType = x extends { outputType: infer O } ? O : never; + + export type pipe< + fn0 extends Fn, + fn1 extends Fn<[getOutputType]> | unset = unset, + fn2 extends Fn<[getOutputType]> | unset = unset, + fn3 extends Fn<[getOutputType]> | unset = unset, + fn4 extends Fn<[getOutputType]> | unset = unset, + fn5 extends Fn<[getOutputType]> | unset = unset, + fn6 extends Fn<[getOutputType]> | unset = unset, + fn7 extends Fn<[getOutputType]> | unset = unset, + fn8 extends Fn<[getOutputType]> | unset = unset, + fn9 extends Fn<[getOutputType]> | unset = unset, + fn10 extends Fn<[getOutputType]> | unset = unset, + fn11 extends Fn<[getOutputType]> | unset = unset, + fn12 extends Fn<[getOutputType]> | unset = unset, + fn13 extends Fn<[getOutputType]> | unset = unset + > = Piped< + Extract< + ExcludeUnset< + [ + fn0, + fn1, + fn2, + fn3, + fn4, + fn5, + fn6, + fn7, + fn8, + fn9, + fn10, + fn11, + fn12, + fn13 + ] + >, + Fn[] + > + >; +} + +export type $< + fn extends Fn, + arg0 extends fn["inputTypes"][0] = unset, + arg1 extends fn["inputTypes"][1] = unset, + arg2 extends fn["inputTypes"][2] = unset, + arg3 extends fn["inputTypes"][3] = unset +> = Extract< + (fn & { + args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; + })["return"], + fn["outputType"] +>; + +type Args = fn["args"]; +type Arg0 = Extract< + Extract[0], + fn["inputTypes"][0] +>; +type Arg1 = Extract< + Extract[1], + fn["inputTypes"][1] +>; +type Arg2 = Extract< + Extract[2], + fn["inputTypes"][2] +>; +type Arg3 = Extract< + Extract[3], + fn["inputTypes"][3] +>; + +/** + * Playground 👇 + */ + +type ExpectNumber = [a]; +// arguments are typed internally: +interface TakeNumAndStr extends Fn<[number, string], boolean> { + works: ExpectNumber>; // ✅ + // @ts-expect-error + fails: ExpectNumber>; + // ~~~~~~~~~~ ❌ + return: true; +} + +interface Div extends Fn<[number, number], number> { + return: NumberImpl.Div, Arg1>; +} + +/** + * Full application + */ + +type t1 = $; // 5 +// ^? +type test1 = Expect>; + +// @ts-expect-error +type err1 = $; +// ~~~ ❌ + +// @ts-expect-error +type err2 = $; +// ~~~ ❌ + +/** + * Partial application in order + */ + +type Div1 = $.partial; +type t2 = $; +// ^? +type test2 = Expect>; +// ^? + +// @ts-expect-error +type t3 = $<$.partial, "2">; +// ~~~ ❌ + +/** + * Partial application different order + */ +type DivBy2 = $.partial; +// ^? +type t4 = $; // 5 ✅ +// ^? +type test4 = Expect>; + +type t5 = $<$.partial, 10, 5>; // ✅ +// ^? +type test5 = Expect>; + +type TakeStr = $.partial; +// ^? $.partial + +// @ts-expect-error +type t8 = $; +// ~~ ❌ + +type TakeNum = $.partial; +// ^?$.partial + +type t7 = $; // ✅ +type test7 = Expect>; + +// @ts-expect-error +type t8 = $; +// ~~~~ ❌ + +/** + * Higher order + */ + +interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { + return: Args extends [infer fn extends Fn, infer tuple] + ? { [key in keyof tuple]: $ } + : never; +} + +type t9 = $, $.partial, [2, 4, 6, 8, 10]>; +// ^? [1, 2, 3, 4, 5] +type test9 = Expect>; + +type Defaults = [a] extends [undefined] ? b : a; + +interface Add extends Fn<[number, number], number> { + return: NumberImpl.Add, Arg1>; +} + +interface Mul extends Fn<[number, number], number> { + return: NumberImpl.Mul, Arg1>; +} + +type ReduceImpl = xs extends [ + infer first, + ...infer rest +] + ? ReduceImpl, rest> + : acc; + +interface Reduce + extends Fn<[Fn<[B, A], B>, B, A[]], B> { + return: Args extends [infer fn extends Fn, infer acc, infer tuple] + ? ReduceImpl + : never; +} + +type t11 = $, Add, 0, [2, 4, 6, 8, 10]>; +// ^? 30 +type test11 = Expect>; + +type t12 = $, Mul, 1, [2, 4, 6, 8, 10]>; +// ^? 3840 +type test12 = Expect>; + +// @ts-expect-error +type t13 = $, Mul, 1, ["2", "4", "6", "8", "10"]>; +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ ❌ + +// @ts-expect-error +type t14 = $; +// ~~~~~~ ❌ + +interface NumToStringReducer extends Fn<[string, number], string> { + return: `${Arg0}${Arg1}`; +} + +interface StringToNumReducer extends Fn<[number, string], number> { + return: NumberImpl.Add, StringImpl.Length>>; +} + +// prettier-ignore +type t15 = $, StringToNumReducer, 1, ["a", "aa", "aaa", "aaaa", "aaaaa"]>; +// ^? 16 +type test15 = Expect>; + +// @ts-expect-error +// prettier-ignore +type t16 = $, NumToStringReducer, 1, ["a", "aa", "aaa", "aaaa", "aaaaa"]>; +// ~~~~~~~~~~~~~~~~~~ ❌ + +interface ToString extends Fn<[number], string> { + return: `${Arg0}`; +} + +interface ToNumber extends Fn<[string], number> { + return: Arg0 extends `${infer N extends number}` ? N : never; +} + +interface Prepend extends Fn<[string, string], string> { + return: `${Arg0}${Arg1}`; +} + +interface ToArray extends Fn<[unknown], [unknown]> { + return: [Arg0]; +} + +type Times10 = $>; + +type t17 = Times10<10>; +// ^? 110 +type test17 = Expect>; + +// @ts-expect-error +type WrongComposition1 = $>; +// ~~~~~~~~~~~~~~ ❌ +// @ts-expect-error +type WrongComposition2 = $>; +// ~~~~~~~~~~~~~~ ❌ + +type Test = $; + +type t18 = Test<"10">; +// ^? 110 +type test18 = Expect>; + +type MapAdd1 = $.partial, $.partial>; +type Sum = $.partial, Add, 0>; + +type t19 = $< + // ^? + $.pipe< + MapAdd1, + Sum, + ToString, + $.partial, + ToNumber, + $.partial, + $.partial, + ToString, + $.partial, + ToNumber + >, + [1, 2, 3, 4] +>; +type test19 = Expect>;