From fc217da6c57a90313e2a80fe60668af197dae599 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Sun, 16 Jul 2023 01:22:37 +0200 Subject: [PATCH 1/8] try new core idea --- src/internals/core/Core.ts | 8 +- src/internals/core/Core2.ts | 138 +++++++++++++++++++++++++++ src/internals/core/impl/MergeArgs.ts | 9 ++ 3 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 src/internals/core/Core2.ts diff --git a/src/internals/core/Core.ts b/src/internals/core/Core.ts index 4f9f630..0f8095a 100644 --- a/src/internals/core/Core.ts +++ b/src/internals/core/Core.ts @@ -1,8 +1,8 @@ import { ExcludePlaceholders, MergeArgs } from "./impl/MergeArgs"; import { Head } from "../helpers"; -declare const rawArgs: unique symbol; -type rawArgs = typeof rawArgs; +export declare const rawArgs: unique symbol; +export type rawArgs = typeof rawArgs; /** * Base interface for all functions. @@ -36,8 +36,8 @@ export interface Fn { return: unknown; } -declare const unset: unique symbol; -declare const _: unique symbol; +export declare const unset: unique symbol; +export declare const _: unique symbol; /** * A placeholder type that can be used to indicate that a parameter is not set. diff --git a/src/internals/core/Core2.ts b/src/internals/core/Core2.ts new file mode 100644 index 0000000..d2e943f --- /dev/null +++ b/src/internals/core/Core2.ts @@ -0,0 +1,138 @@ +import { _, unset } from "./Core"; +import { ExcludePlaceholders, ExcludeUnset, MergeArgs } from "./impl/MergeArgs"; +import * as NumberImpl from "../numbers/impl/numbers"; + +/** + * Core + */ + +export interface Fn { + inputTypes: input; + args: unknown; + return: unknown; +} + +interface Ap extends Fn { + name: "Ap"; + + inputTypes: fn["inputTypes"]; + + argsArray: Extract; + allArgs: [...partialArgs, ...this["argsArray"]]; + + expectedArgsCount: fn["inputTypes"]["length"]; + providedArgsCount: ExcludePlaceholders["length"]; + + return: NumberImpl.Compare< + this["providedArgsCount"], + this["expectedArgsCount"] + > extends 1 | 0 + ? Apply> + : Ap; +} + +export type Apply = (fn & { + args: args; +})["return"]; + +export type $< + fn extends Fn, + arg0 extends fn["inputTypes"][0] | _ | unset = unset, + arg1 extends fn["inputTypes"][1] | _ | unset = unset, + arg2 extends fn["inputTypes"][2] | _ | unset = unset, + arg3 extends fn["inputTypes"][3] | _ | unset = unset +> = ((fn extends { name: "Ap" } ? fn : Ap) & { + args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; +})["return"]; + +type Args = Extract; +type Arg0 = Extract[0]; +type Arg1 = Extract[1]; +type Arg2 = Extract[2]; +type Arg3 = Extract[3]; + +type ExpectNumber = [a]; +// arguments are typed internally: +interface TypedArgsTest extends Fn<[number, string]> { + works: ExpectNumber>; // ✅ + fails: ExpectNumber>; + // ~~~~~~~~~~ ❌ + return: true; +} + +interface Div extends Fn<[number, number]> { + return: NumberImpl.Div, Arg1>; +} + +/** + * Full application + */ + +type x = $; // 5 +// ^? +type y = $; +// ~~~ ❌ +type z = $; +// ~~~ ❌ + +/** + * Partial application in order + */ + +type Div1 = $; +type Three = $; +// ^? +type w = $<$, "2">; +// ~~~ ❌ + +/** + * Partial application different order order + */ +type DivBy2 = $; +// ^? +type q = $; // 5 ✅ +// ^? +type r = $<$, 10, 5>; // ✅ +// ^? + +/** + * Higher order + */ + +interface Map extends Fn<[Fn, any[]]> { + return: Args extends [infer fn extends Fn, infer tuple] + ? { [key in keyof tuple]: $ } + : never; +} + +type z2 = $, [2, 4, 6, 8, 10]>; +// ^? + +type ReduceImpl = xs extends [ + infer first, + ...infer rest +] + ? ReduceImpl, rest> + : acc; + +interface Reduce extends Fn<[Fn, any, any[]]> { + return: Args extends [infer fn extends Fn, infer acc, infer tuple] + ? ReduceImpl + : never; +} + +interface Add extends Fn<[number, number]> { + return: NumberImpl.Add, Arg1>; +} + +interface Mul extends Fn<[number, number]> { + return: NumberImpl.Mul, Arg1>; +} + +type reduced1 = $; +// ^? +type reduced2 = $; +// ^? + +type reducedOops = $; +// ~~~~~~ ❌ diff --git a/src/internals/core/impl/MergeArgs.ts b/src/internals/core/impl/MergeArgs.ts index 2873e13..e892aa1 100644 --- a/src/internals/core/impl/MergeArgs.ts +++ b/src/internals/core/impl/MergeArgs.ts @@ -10,6 +10,15 @@ export type ExcludePlaceholders = xs extends [ : ExcludePlaceholders : output; +export type ExcludeUnset = xs extends [ + infer first, + ...infer rest +] + ? Equal extends true + ? ExcludeUnset + : ExcludeUnset + : output; + type MergeArgsRec< pipedArgs extends any[], partialArgs extends any[], From 37f136549c0e54bd105004c0912a2d1f6d14524e Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Sun, 16 Jul 2023 13:42:56 +0200 Subject: [PATCH 2/8] Add function's return type --- src/internals/core/Core2.ts | 123 ++++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 25 deletions(-) diff --git a/src/internals/core/Core2.ts b/src/internals/core/Core2.ts index d2e943f..d0e1800 100644 --- a/src/internals/core/Core2.ts +++ b/src/internals/core/Core2.ts @@ -1,28 +1,44 @@ 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 { Iterator } from "../helpers"; /** * Core */ -export interface Fn { +export interface Fn { inputTypes: input; + outputType: output; args: unknown; return: unknown; } +type Drop< + xs extends readonly any[], + n extends number, + dropped extends readonly any[] = [] +> = n extends dropped["length"] + ? xs + : xs extends readonly [infer first, ...infer tail] + ? Drop + : []; + interface Ap extends Fn { name: "Ap"; - inputTypes: fn["inputTypes"]; - argsArray: Extract; allArgs: [...partialArgs, ...this["argsArray"]]; expectedArgsCount: fn["inputTypes"]["length"]; providedArgsCount: ExcludePlaceholders["length"]; + inputTypes: Drop< + fn["inputTypes"], + ExcludePlaceholders["length"] + >; + return: NumberImpl.Compare< this["providedArgsCount"], this["expectedArgsCount"] @@ -37,13 +53,16 @@ export type Apply = (fn & { export type $< fn extends Fn, - arg0 extends fn["inputTypes"][0] | _ | unset = unset, - arg1 extends fn["inputTypes"][1] | _ | unset = unset, - arg2 extends fn["inputTypes"][2] | _ | unset = unset, - arg3 extends fn["inputTypes"][3] | _ | unset = unset -> = ((fn extends { name: "Ap" } ? fn : Ap) & { - args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; -})["return"]; + 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 extends { name: "Ap" } ? fn : Ap) & { + args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; + })["return"], + fn["outputType"] +>; type Args = Extract; type Arg0 = Extract[0]; @@ -53,14 +72,14 @@ type Arg3 = Extract[3]; type ExpectNumber = [a]; // arguments are typed internally: -interface TypedArgsTest extends Fn<[number, string]> { +interface TakeNumAndStr extends Fn<[number, string], boolean> { works: ExpectNumber>; // ✅ fails: ExpectNumber>; // ~~~~~~~~~~ ❌ return: true; } -interface Div extends Fn<[number, number]> { +interface Div extends Fn<[number, number], number> { return: NumberImpl.Div, Arg1>; } @@ -86,7 +105,7 @@ type w = $<$, "2">; // ~~~ ❌ /** - * Partial application different order order + * Partial application different order */ type DivBy2 = $; // ^? @@ -95,6 +114,16 @@ type q = $; // 5 ✅ type r = $<$, 10, 5>; // ✅ // ^? +type TakeStr = $; +// ^? Ap +type e = $; +// ~~ ❌ + +type TakeNum = $; +// ^?Ap +type s = $; +// ~~ FIXME + /** * Higher order */ @@ -106,7 +135,15 @@ interface Map extends Fn<[Fn, any[]]> { } type z2 = $, [2, 4, 6, 8, 10]>; -// ^? +// ^? [1, 2, 3, 4, 5] + +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, @@ -115,24 +152,60 @@ type ReduceImpl = xs extends [ ? ReduceImpl, rest> : acc; -interface Reduce extends Fn<[Fn, any, any[]]> { +interface Reduce extends Fn<[Fn<[B, A], B>, B, A[]], B> { return: Args extends [infer fn extends Fn, infer acc, infer tuple] ? ReduceImpl : never; } -interface Add extends Fn<[number, number]> { - return: NumberImpl.Add, Arg1>; +type reduced1 = $, Add, 0, [2, 4, 6, 8, 10]>; +// ^? 30 +type reduced2 = $, Mul, 1, [2, 4, 6, 8, 10]>; +// ^? 3840 +type reduced3 = $, Mul, 1, ["2", "4", "6", "8", "10"]>; +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ ❌ +type reducedOops = $; +// ~~~~~~ ❌ + +interface NumToStringReducer extends Fn<[string, number], string> { + return: `${Arg0}${Arg1}`; } -interface Mul extends Fn<[number, number]> { - return: NumberImpl.Mul, Arg1>; +interface StringToNumReducer extends Fn<[number, string], number> { + return: NumberImpl.Add, StringImpl.Length>>; } -type reduced1 = $; -// ^? -type reduced2 = $; -// ^? +// prettier-ignore +type reduced4 = $, StringToNumReducer, 1, ["a", "aa", "aaa", "aaaa", "aaaaa"]>; +// ^? 16 -type reducedOops = $; -// ~~~~~~ ❌ +// prettier-ignore +type reduced5 = $, 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: this["args"] extends [ + infer first extends string, + infer str extends string + ] + ? `${first}${str}` + : never; +} + +type Times10 = $>>; + +type WrongComposition1 = $>; +// ~~~~~~~~~~~~~~ ❌ +type WrongComposition2 = $>; +// ~~~~~~~~~~~~~~ ❌ + +type test1 = Times10<10>; +// ^? 110 From 7363f6df9527eb7e0eb0289a261025c2906834c7 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Sun, 16 Jul 2023 15:55:18 +0200 Subject: [PATCH 3/8] Some improvements --- src/internals/core/Core2.ts | 103 +++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 32 deletions(-) diff --git a/src/internals/core/Core2.ts b/src/internals/core/Core2.ts index d0e1800..3493ff6 100644 --- a/src/internals/core/Core2.ts +++ b/src/internals/core/Core2.ts @@ -2,7 +2,6 @@ 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 { Iterator } from "../helpers"; /** * Core @@ -25,6 +24,21 @@ type Drop< ? Drop : []; +type ExcludePlaceholdersFromInputTypes< + inputTypes extends any[], + partialArgs extends any[], + result extends any[] = [] +> = [inputTypes, partialArgs] extends [ + [infer fInput, ...infer rInput], + [infer fPartial, ...infer rPartial] +] + ? ExcludePlaceholdersFromInputTypes< + rInput, + rPartial, + fPartial extends _ ? [...result, fInput] : result + > + : [...result, ...inputTypes]; + interface Ap extends Fn { name: "Ap"; @@ -34,15 +48,18 @@ interface Ap extends Fn { expectedArgsCount: fn["inputTypes"]["length"]; providedArgsCount: ExcludePlaceholders["length"]; - inputTypes: Drop< - fn["inputTypes"], - ExcludePlaceholders["length"] - >; + inputTypes: ExcludePlaceholdersFromInputTypes; + + outputType: fn["outputType"]; - return: NumberImpl.Compare< + isFullyApplied: NumberImpl.Compare< this["providedArgsCount"], this["expectedArgsCount"] > extends 1 | 0 + ? true + : false; + + return: this["isFullyApplied"] extends true ? Apply> : Ap; } @@ -51,24 +68,40 @@ export type Apply = (fn & { args: args; })["return"]; +type AnyAp = Ap; + 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 extends { name: "Ap" } ? fn : Ap) & { - args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; - })["return"], - fn["outputType"] + arg0 extends fn["inputTypes"][0] | AnyAp | _ = unset, + arg1 extends fn["inputTypes"][1] | AnyAp | _ = unset, + arg2 extends fn["inputTypes"][2] | AnyAp | _ = unset, + arg3 extends fn["inputTypes"][3] | AnyAp | _ = unset, + ap extends AnyAp = fn extends { name: "Ap" } ? fn : Ap +> = (ap & { + args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; +})["return"]; + +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] >; -type Args = Extract; -type Arg0 = Extract[0]; -type Arg1 = Extract[1]; -type Arg2 = Extract[2]; -type Arg3 = Extract[3]; +/** + * Playground 👇 + */ type ExpectNumber = [a]; // arguments are typed internally: @@ -111,6 +144,7 @@ type DivBy2 = $; // ^? type q = $; // 5 ✅ // ^? + type r = $<$, 10, 5>; // ✅ // ^? @@ -121,20 +155,21 @@ type e = $; type TakeNum = $; // ^?Ap -type s = $; -// ~~ FIXME +type s = $; // ✅ +type d = $; +// ~~~~ ❌ /** * Higher order */ -interface Map extends Fn<[Fn, any[]]> { +interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { return: Args extends [infer fn extends Fn, infer tuple] ? { [key in keyof tuple]: $ } : never; } -type z2 = $, [2, 4, 6, 8, 10]>; +type z2 = $, $, [2, 4, 6, 8, 10]>; // ^? [1, 2, 3, 4, 5] interface Add extends Fn<[number, number], number> { @@ -192,20 +227,24 @@ interface ToNumber extends Fn<[string], number> { } interface Prepend extends Fn<[string, string], string> { - return: this["args"] extends [ - infer first extends string, - infer str extends string - ] - ? `${first}${str}` - : never; + return: `${Arg0}${Arg1}`; +} + +interface ToArray extends Fn<[any], [any]> { + return: [Arg0]; } -type Times10 = $>>; +type Times10 = $>; + +type test1 = Times10<10>; +// ^? 110 type WrongComposition1 = $>; // ~~~~~~~~~~~~~~ ❌ type WrongComposition2 = $>; // ~~~~~~~~~~~~~~ ❌ -type test1 = Times10<10>; +type Test = $; + +type test2 = Test<"10">; // ^? 110 From 8dc9ff2059d79dacd608071eedd02dd6878ef389 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Sun, 16 Jul 2023 17:16:23 +0200 Subject: [PATCH 4/8] use unknown instead of any to preserve constraints --- src/internals/core/Core2.ts | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/internals/core/Core2.ts b/src/internals/core/Core2.ts index 3493ff6..ab7dec5 100644 --- a/src/internals/core/Core2.ts +++ b/src/internals/core/Core2.ts @@ -7,7 +7,7 @@ import * as StringImpl from "../strings/impl/strings"; * Core */ -export interface Fn { +export interface Fn { inputTypes: input; outputType: output; args: unknown; @@ -15,9 +15,9 @@ export interface Fn { } type Drop< - xs extends readonly any[], + xs extends readonly unknown[], n extends number, - dropped extends readonly any[] = [] + dropped extends readonly unknown[] = [] > = n extends dropped["length"] ? xs : xs extends readonly [infer first, ...infer tail] @@ -25,9 +25,9 @@ type Drop< : []; type ExcludePlaceholdersFromInputTypes< - inputTypes extends any[], - partialArgs extends any[], - result extends any[] = [] + inputTypes extends unknown[], + partialArgs extends unknown[], + result extends unknown[] = [] > = [inputTypes, partialArgs] extends [ [infer fInput, ...infer rInput], [infer fPartial, ...infer rPartial] @@ -39,10 +39,10 @@ type ExcludePlaceholdersFromInputTypes< > : [...result, ...inputTypes]; -interface Ap extends Fn { +interface Ap extends Fn { name: "Ap"; - argsArray: Extract; + argsArray: Extract; allArgs: [...partialArgs, ...this["argsArray"]]; expectedArgsCount: fn["inputTypes"]["length"]; @@ -64,11 +64,11 @@ interface Ap extends Fn { : Ap; } -export type Apply = (fn & { +export type Apply = (fn & { args: args; })["return"]; -type AnyAp = Ap; +type AnyAp = Ap; export type $< fn extends Fn, @@ -83,19 +83,19 @@ export type $< type Args = fn["args"]; type Arg0 = Extract< - Extract[0], + Extract[0], fn["inputTypes"][0] >; type Arg1 = Extract< - Extract[1], + Extract[1], fn["inputTypes"][1] >; type Arg2 = Extract< - Extract[2], + Extract[2], fn["inputTypes"][2] >; type Arg3 = Extract< - Extract[3], + Extract[3], fn["inputTypes"][3] >; @@ -163,7 +163,7 @@ type d = $; * Higher order */ -interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { +interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { return: Args extends [infer fn extends Fn, infer tuple] ? { [key in keyof tuple]: $ } : never; @@ -187,7 +187,8 @@ type ReduceImpl = xs extends [ ? ReduceImpl, rest> : acc; -interface Reduce extends Fn<[Fn<[B, A], B>, B, A[]], B> { +interface Reduce + extends Fn<[Fn<[B, A], B>, B, A[]], B> { return: Args extends [infer fn extends Fn, infer acc, infer tuple] ? ReduceImpl : never; @@ -230,7 +231,7 @@ interface Prepend extends Fn<[string, string], string> { return: `${Arg0}${Arg1}`; } -interface ToArray extends Fn<[any], [any]> { +interface ToArray extends Fn<[unknown], [unknown]> { return: [Arg0]; } From 0f510a09e0307fcf3aaee198c9d002dbfcf05f63 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Sun, 16 Jul 2023 18:47:34 +0200 Subject: [PATCH 5/8] 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>; From 2adf1cc362441afa7dabb28fdaee324b18e34ee2 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Sun, 16 Jul 2023 19:27:21 +0200 Subject: [PATCH 6/8] Add more examples --- src/internals/core/Core3.ts | 40 +++++++++++++++++----------- src/internals/core/impl/MergeArgs.ts | 2 +- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/internals/core/Core3.ts b/src/internals/core/Core3.ts index 3c3d08d..a5a9472 100644 --- a/src/internals/core/Core3.ts +++ b/src/internals/core/Core3.ts @@ -232,13 +232,13 @@ type t8 = $; * Higher order */ -interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { +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]>; +type t9 = $, $.partial, [2, 4, 6, 8, 10]>; // ^? [1, 2, 3, 4, 5] type test9 = Expect>; @@ -259,23 +259,22 @@ type ReduceImpl = xs extends [ ? ReduceImpl, rest> : acc; -interface Reduce - extends Fn<[Fn<[B, A], B>, B, A[]], B> { +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]>; +type t11 = $, Add, 0, [2, 4, 6, 8, 10]>; // ^? 30 type test11 = Expect>; -type t12 = $, Mul, 1, [2, 4, 6, 8, 10]>; +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"]>; +type t13 = $, Mul, 1, ["2", "4", "6", "8", "10"]>; // ~~~~~~~~~~~~~~~~~~~~~~~~~~ ❌ // @ts-expect-error @@ -335,23 +334,34 @@ type t18 = Test<"10">; // ^? 110 type test18 = Expect>; -type MapAdd1 = $.partial, $.partial>; -type Sum = $.partial, Add, 0>; +type Sum = $.partial, Add, 0>; -type t19 = $< - // ^? +type Composition = $< $.pipe< - MapAdd1, + $.partial, $.partial>, + $.partial< + Map, + $.pipe< + $.partial, + $.partial, + ToString, + $.partial, + ToNumber + > + >, Sum, ToString, $.partial, ToNumber, - $.partial, + $.partial, $.partial, ToString, $.partial, ToNumber >, - [1, 2, 3, 4] + T >; -type test19 = Expect>; + +type t19 = Composition<[1, 2, 3, 4]>; +// ^? +type test19 = Expect>; diff --git a/src/internals/core/impl/MergeArgs.ts b/src/internals/core/impl/MergeArgs.ts index e892aa1..3c40d53 100644 --- a/src/internals/core/impl/MergeArgs.ts +++ b/src/internals/core/impl/MergeArgs.ts @@ -14,7 +14,7 @@ export type ExcludeUnset = xs extends [ infer first, ...infer rest ] - ? Equal extends true + ? first extends unset ? ExcludeUnset : ExcludeUnset : output; From 318759a03c72bce33981dc5ba5de65152ee5f7b2 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Mon, 17 Jul 2023 19:27:40 +0200 Subject: [PATCH 7/8] try creating larger compositions --- src/internals/core/Core2.ts | 78 +++++++++++++ src/internals/core/Core3.ts | 212 ++++++++++++++++++++++-------------- test/tuples.test.ts | 34 +++++- 3 files changed, 242 insertions(+), 82 deletions(-) diff --git a/src/internals/core/Core2.ts b/src/internals/core/Core2.ts index 800dd5b..b5b6409 100644 --- a/src/internals/core/Core2.ts +++ b/src/internals/core/Core2.ts @@ -55,6 +55,52 @@ interface Ap extends Fn { : Ap; } +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>; +} + +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 Apply = (fn & { args: args; })["return"]; @@ -73,18 +119,22 @@ export type $< })["return"]; 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] @@ -272,3 +322,31 @@ type Test = $; type t18 = Test<"10">; // ^? 110 type test18 = Expect>; + +type Sum = $, Add, 0>; + +type TransformNumber = Pipe< + $, + $, + ToString, + $, + ToNumber +>; + +type Composed = Pipe< + $, $>, + $, TransformNumber>, + Sum, + ToString, + $, + ToNumber, + $, + $, + ToString, + $, + ToNumber +>; + +type t19 = $; +// ^? +type test19 = Expect>; diff --git a/src/internals/core/Core3.ts b/src/internals/core/Core3.ts index a5a9472..6e79503 100644 --- a/src/internals/core/Core3.ts +++ b/src/internals/core/Core3.ts @@ -1,5 +1,5 @@ import { _, unset } from "./Core"; -import { ExcludePlaceholders, ExcludeUnset, MergeArgs } from "./impl/MergeArgs"; +import { ExcludeUnset, MergeArgs } from "./impl/MergeArgs"; import * as NumberImpl from "../numbers/impl/numbers"; import * as StringImpl from "../strings/impl/strings"; import { Equal, Expect } from "../helpers"; @@ -8,8 +8,18 @@ import { Equal, Expect } from "../helpers"; * Core */ -export interface Fn { - inputTypes: input; +type Contra = (a: t) => void; + +type GetInputTypes = fn extends { + inputTypes: Contra; +} + ? I + : never; + +type GetOutputType = fn extends { outputType: infer O } ? O : never; + +export interface Fn { + inputTypes: Contra; outputType: output; args: unknown; return: unknown; @@ -41,7 +51,9 @@ interface Applied argsArray: Extract; allArgs: [...partialArgs, ...this["argsArray"]]; - inputTypes: ExcludePlaceholdersFromInputTypes; + inputTypes: Contra< + ExcludePlaceholdersFromInputTypes, partialArgs> + >; outputType: fn["outputType"]; @@ -71,60 +83,82 @@ interface Piped extends Fn { 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 + arg0 extends GetInputTypes[0] | _ = unset, + arg1 extends GetInputTypes[1] | _ = unset, + arg2 extends GetInputTypes[2] | _ = unset, + arg3 extends GetInputTypes[3] | _ = unset > = Applied>; - type getOutputType = x extends { outputType: infer O } ? O : never; + export type call< + fn extends Fn, + arg0 extends GetInputTypes[0] = unset, + arg1 extends GetInputTypes[1] = unset, + arg2 extends GetInputTypes[2] = unset, + arg3 extends GetInputTypes[3] = unset + > = Extract< + (fn & { + args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; + })["return"], + fn["outputType"] + >; 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 + 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, + fn14 extends Fn<[GetOutputType]> | unset = unset, + fn15 extends Fn<[GetOutputType]> | unset = unset, + fn16 extends Fn<[GetOutputType]> | unset = unset, + fn17 extends Fn<[GetOutputType]> | unset = unset, + fn18 extends Fn<[GetOutputType]> | unset = unset, + fn19 extends Fn<[GetOutputType]> | unset = unset, + fn20 extends Fn<[GetOutputType]> | unset = unset > = Piped< - Extract< - ExcludeUnset< - [ - fn0, - fn1, - fn2, - fn3, - fn4, - fn5, - fn6, - fn7, - fn8, - fn9, - fn10, - fn11, - fn12, - fn13 - ] - >, - Fn[] + ExcludeUnset< + [ + fn0, + fn1, + fn2, + fn3, + fn4, + fn5, + fn6, + fn7, + fn8, + fn9, + fn10, + fn11, + fn12, + fn13, + fn14, + fn15, + fn16, + fn17, + fn18, + fn19, + fn20 + ] > >; } 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 + arg0 extends GetInputTypes[0] = unset, + arg1 extends GetInputTypes[1] = unset, + arg2 extends GetInputTypes[2] = unset, + arg3 extends GetInputTypes[3] = unset > = Extract< (fn & { args: ExcludeUnset<[arg0, arg1, arg2, arg3]>; @@ -135,19 +169,19 @@ export type $< type Args = fn["args"]; type Arg0 = Extract< Extract[0], - fn["inputTypes"][0] + GetInputTypes[0] >; type Arg1 = Extract< Extract[1], - fn["inputTypes"][1] + GetInputTypes[1] >; type Arg2 = Extract< Extract[2], - fn["inputTypes"][2] + GetInputTypes[2] >; type Arg3 = Extract< Extract[3], - fn["inputTypes"][3] + GetInputTypes[3] >; /** @@ -232,7 +266,7 @@ type t8 = $; * Higher order */ -interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { +interface Map extends Fn<[Fn<[A], B>, A[]], B[]> { return: Args extends [infer fn extends Fn, infer tuple] ? { [key in keyof tuple]: $ } : never; @@ -259,7 +293,7 @@ type ReduceImpl = xs extends [ ? ReduceImpl, rest> : acc; -interface Reduce extends Fn<[Fn<[B, A], B>, B, A[]], B> { +interface Reduce extends Fn<[Fn<[B, A], B>, B, A[]], B> { return: Args extends [infer fn extends Fn, infer acc, infer tuple] ? ReduceImpl : never; @@ -275,7 +309,7 @@ type test12 = Expect>; // @ts-expect-error type t13 = $, Mul, 1, ["2", "4", "6", "8", "10"]>; -// ~~~~~~~~~~~~~~~~~~~~~~~~~~ ❌ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ ❌ // @ts-expect-error type t14 = $; @@ -311,7 +345,7 @@ interface Prepend extends Fn<[string, string], string> { return: `${Arg0}${Arg1}`; } -interface ToArray extends Fn<[unknown], [unknown]> { +interface ToArray extends Fn<[T], [T]> { return: [Arg0]; } @@ -322,6 +356,7 @@ type t17 = Times10<10>; type test17 = Expect>; // @ts-expect-error +// prettier-ignore type WrongComposition1 = $>; // ~~~~~~~~~~~~~~ ❌ // @ts-expect-error @@ -334,34 +369,49 @@ type t18 = Test<"10">; // ^? 110 type test18 = Expect>; -type Sum = $.partial, Add, 0>; - -type Composition = $< - $.pipe< - $.partial, $.partial>, - $.partial< - Map, - $.pipe< - $.partial, - $.partial, - ToString, - $.partial, - ToNumber - > - >, - Sum, - ToString, - $.partial, - ToNumber, - $.partial, - $.partial, - ToString, - $.partial, - ToNumber +type Sum = $.partial; + +type Composed = $.pipe< + $.partial>, + $.partial< + Map, + $.pipe< + $.partial, + $.partial, + ToString, + $.partial, + ToNumber + > + >, + $.partial< + Map, + $.pipe< + $.partial, + $.partial, + ToString, + $.partial, + ToNumber + > + >, + $.partial< + Map, + $.pipe< + $.partial, + $.partial, + ToString, + $.partial, + ToNumber + > >, - T + $.partial>, + Sum, + ToString, + $.partial, + ToNumber, + $.partial, + $.partial >; -type t19 = Composition<[1, 2, 3, 4]>; +type t19 = $; // ^? -type test19 = Expect>; +type test19 = Expect>; diff --git a/test/tuples.test.ts b/test/tuples.test.ts index 6a4a8b3..52fdad8 100644 --- a/test/tuples.test.ts +++ b/test/tuples.test.ts @@ -1,5 +1,5 @@ import { Booleans } from "../src/internals/booleans/Booleans"; -import { Call, Fn, Pipe, _ } from "../src/internals/core/Core"; +import { Call, ComposeLeft, Fn, Pipe, _ } from "../src/internals/core/Core"; import { Equal, Expect } from "../src/internals/helpers"; import { Numbers } from "../src/internals/numbers/Numbers"; import { Strings } from "../src/internals/strings/Strings"; @@ -569,3 +569,35 @@ describe("Tuples", () => { type test3 = Expect>; }); }); + +type Sum = Tuples.Reduce; + +type Comp1 = ComposeLeft< + [ + Tuples.Map>, + Tuples.Map< + ComposeLeft< + [ + Numbers.Add<1>, + Numbers.Mul<3>, + Strings.ToString, + Strings.Prepend<"1">, + Strings.ToNumber + ] + > + >, + Tuples.Map>, + Sum, + Strings.ToString, + Strings.Prepend<"1">, + Strings.ToNumber, + Numbers.Mul<10>, + Numbers.Div<_, 2> + ] +>; + +type Composition = ComposeLeft<[Comp1, Tuples.Append<_, []>, Comp1]>; + +type t19 = Call; +// ^? +type test19 = Expect>; From 289a0d358ba9ccfb3ca88351c30b61c262088080 Mon Sep 17 00:00:00 2001 From: gvergnaud Date: Wed, 28 Aug 2024 22:24:27 -0400 Subject: [PATCH 8/8] feat: add ability to return an error in pipe --- src/internals/core/Core3.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/internals/core/Core3.ts b/src/internals/core/Core3.ts index 6e79503..da8dcad 100644 --- a/src/internals/core/Core3.ts +++ b/src/internals/core/Core3.ts @@ -64,12 +64,16 @@ namespace Tuple { export type Last = xs extends [...any, infer last] ? last : never; } -type ApplyLeftToRight = fns extends [ +type ApplyLeftToRight = fns extends [ infer fn extends Fn, ...infer restFns ] - ? ApplyLeftToRight<[Apply], restFns> - : x[0]; + ? Apply extends infer output + ? output extends $.Error + ? output + : ApplyLeftToRight<[output], restFns> + : never + : args[0]; interface Piped extends Fn { name: "$.pipe"; @@ -151,6 +155,12 @@ namespace $ { ] > >; + + const brand = Symbol.for("@hotscript/brand"); + + export interface Error { + [brand]: "@hotscript/error"; + } } export type $<