diff --git a/core/base/src/constants/guardians.ts b/core/base/src/constants/guardians.ts index b408077f9..9ac5bd732 100644 --- a/core/base/src/constants/guardians.ts +++ b/core/base/src/constants/guardians.ts @@ -1,5 +1,5 @@ import type { MapLevels} from './../utils/index.js'; -import { constMap, column, cartesianRightRecursive } from './../utils/index.js'; +import { constMap, filterIndexes, zip, cartesianRightRecursive } from './../utils/index.js'; import type { Network } from './networks.js'; // prettier-ignore @@ -30,8 +30,8 @@ const guardianKeyAndNameEntries = [[ ]] ] as const satisfies MapLevels<[Network, string, string]>; -export const guardianKeys = column(cartesianRightRecursive(guardianKeyAndNameEntries), 1); -export const guardianNames = column(cartesianRightRecursive(guardianKeyAndNameEntries), 2); +export const [guardianKeys, guardianNames] = + filterIndexes(zip(cartesianRightRecursive(guardianKeyAndNameEntries)), [1, 2]); export const guardianNameToKey = constMap(guardianKeyAndNameEntries, [[0, 2], 1]); export const guardianKeyToName = constMap(guardianKeyAndNameEntries, [1, [0, 2]]); diff --git a/core/base/src/utils/array.ts b/core/base/src/utils/array.ts index ac69bae29..ddd9bf49d 100644 --- a/core/base/src/utils/array.ts +++ b/core/base/src/utils/array.ts @@ -1,63 +1,105 @@ -import type { RoArray, RoArray2D, IsUnion } from './metaprogramming.js'; +import type { RoPair, RoTuple, RoArray, Extends, Xor, Not } from './metaprogramming.js'; -export const range = (length: number) => [...Array(length).keys()]; +export type RoTuple2D = RoTuple>; +export type RoArray2D = RoArray>; -//TODO the intent here is that number represents a number literal, but strictly speaking -// the type allows for unions of number literals (and an array of such unions) -//The reason for not just sticking to unions is that unions lose order information which is -// relevant in some cases (and iterating over them is a pain). -export type IndexEs = number | RoArray; +type TupleRangeImpl = + A["length"] extends L + ? A + : TupleRangeImpl; -export type ElementIndexPairs = - [...{ [K in keyof T]: K extends `${infer N extends number}` ? [T[K], N] : never }]; +export type TupleRange = + number extends L + ? never + : L extends any + ? TupleRangeImpl + : never; -export const elementIndexPairs = (arr: T) => - range(arr.length).map(i => [arr[i], i]) as ElementIndexPairs; +export type Range = + L extends any + ? number extends L + ? number[] + : TupleRange + : never; -export type Entries = +export type TupleWithLength = + TupleRange extends infer R extends RoArray + ? [...{ [K in keyof R]: T }] + : never; + +export type RoTupleWithLength = Readonly>; + +export const range = (length: L) => + [...Array(length).keys()] as Range; + +//capitalization to highlight that this is intended to be a literal or a union of literals +export type IndexEs = number; + +//utility type to reduce boilerplate of iteration code by replacing: +// `T extends readonly [infer Head extends T[number], ...infer Tail extends RoTuple]` +//with just: +// `T extends HeadTail` +//this also avoids the somewhat common mistake of accidentally dropping the readonly modifier +export type HeadTail> = + readonly [Head, ...Tail]; + +export type TupleEntries = [...{ [K in keyof T]: K extends `${infer N extends number}` ? [N, T[K]] : never }]; -export const entries = (arr: T) => - range(arr.length).map(i => [i, arr[i]]) as Entries; +//const aware version of Array.entries +export type Entries = + T extends RoTuple + ? TupleEntries + : T extends RoArray + ? [number, U][] + : never; + +export function entries(arr: T): TupleEntries; +export function entries(arr: T): Entries; +export function entries(arr: readonly any[]): [number, any][] { + return [...arr.entries()]; +} -export type Flatten = - T extends readonly [infer Head, ...infer Tail extends RoArray] - ? Head extends RoArray - ? [...Head, ...Flatten] - : [Head, ...Flatten] +export type IsArray = T extends RoArray ? true : false; +export type IsFlat = true extends IsArray ? false : true; + +export type TupleFlatten = + T extends HeadTail + ? Head extends RoTuple + ? [...Head, ...TupleFlatten] + : [Head, ...TupleFlatten] : []; -export type InnerFlatten = - [...{ [K in keyof T]: +type StripArray = T extends RoArray ? E : T; + +export type Flatten = + A extends RoTuple + ? TupleFlatten + : StripArray[]; + +export const flatten = (arr: A) => + arr.flat() as Flatten; + +export type InnerFlatten = + [...{ [K in keyof A]: K extends `${number}` - ? T[K] extends RoArray - ? Flatten - : T[K] + ? A[K] extends RoArray + ? Flatten + : A[K] : never }]; -export type IsFlat = - T extends readonly [infer Head, ...infer Tail extends RoArray] - ? Head extends RoArray - ? false - : IsFlat - : true; - -export type Unflatten = - [...{ [K in keyof T]: K extends `${number}` ? [T[K]] : never }]; - -export type AllSameLength = - T extends readonly [infer Head extends RoArray, ...infer Tail extends RoArray2D] - ? L extends void - ? AllSameLength - : Head["length"] extends L - ? AllSameLength - : false - : true; - -export type IsRectangular = - //1d array is rectangular - T extends RoArray2D ? AllSameLength : IsFlat; +export type Unflatten = + [...{ [K in keyof A]: K extends `${number}` ? [A[K]] : never }]; + +export type IsRectangular = + T extends RoTuple2D + ? T extends HeadTail + ? Tail extends readonly [] + ? true //a column is rectangular + : Tail[number]["length"] extends Head["length"] ? true : false + : true //empty is rectangular + : true; //a row is rectangular export type Column = [...{ [K in keyof A]: K extends `${number}` ? A[K][I] : never }]; @@ -65,48 +107,66 @@ export type Column = export const column = (tupArr: A, index: I) => tupArr.map((tuple) => tuple[index]) as Column; -export type Zip = - //TODO remove, find max length, and return undefined for elements in shorter arrays - A["length"] extends 0 - ? [] - : IsRectangular extends true - ? A[0] extends infer Head extends RoArray +export type TupleZip = + IsRectangular extends true + ? T[0] extends infer Head extends RoTuple ? [...{ [K in keyof Head]: K extends `${number}` - ? [...{ [K2 in keyof A]: K extends keyof A[K2] ? A[K2][K] : never }] + ? [...{ [K2 in keyof T]: K extends keyof T[K2] ? T[K2][K] : never }] : never }] : [] - : never + : never; + +export type Zip = + A extends RoTuple2D + ? TupleZip + : Flatten[number][][]; export const zip = (arr: Args) => range(arr[0]!.length).map(col => range(arr.length).map(row => arr[row]![col]) - ) as unknown as ([Zip] extends [never] ? RoArray2D : Zip); + ) as Zip; //extracts elements with the given indexes in the specified order, explicitly forbid unions -export type OnlyIndexes = - IsUnion extends false - ? I extends number - ? OnlyIndexes - : I extends readonly [infer Head extends number, ...infer Tail extends RoArray] - ? E[Head] extends undefined - ? OnlyIndexes - : [E[Head], ...OnlyIndexes] - : [] - : never; +export type TuplePickWithOrder> = + I extends HeadTail + ? A[Head] extends undefined + ? TuplePickWithOrder + : [A[Head], ...TuplePickWithOrder] + : []; -type ExcludeIndexesImpl = - T extends readonly [infer Head, ...infer Tail] - ? Head extends readonly [infer I extends number, infer V] - ? I extends C - ? ExcludeIndexesImpl - : [V, ...ExcludeIndexesImpl] +export type PickWithOrder> = + [A, I] extends [infer T extends RoTuple, infer TI extends RoTuple] + ? TuplePickWithOrder + : A; + +export const pickWithOrder = + >(arr: A, indexes: I) => + indexes.map((i) => arr[i]) as PickWithOrder; + +type FilterIndexesImpl = + T extends HeadTail + ? Head extends RoPair + ? Not, Extends>> extends true + ? [V, ...FilterIndexesImpl] + : FilterIndexesImpl : never : []; -export type ExcludeIndexes = - ExcludeIndexesImpl, C extends RoArray ? C[number] : C>; +export type FilterIndexes = + A extends infer T extends RoTuple + ? FilterIndexesImpl, I, E> + : A; + +export const filterIndexes = < + const T extends RoArray, + const I extends RoArray, + const E extends boolean = false +>(arr: T, indexes: I, exclude?: E) => { + const indexSet = new Set(Array.isArray(indexes) ? indexes : [indexes]); + return arr.filter((_,i) => indexSet.has(i) !== exclude) as FilterIndexes; +}; export type Cartesian = L extends RoArray diff --git a/core/base/src/utils/mapping.ts b/core/base/src/utils/mapping.ts index 82b0617a6..e6fbaf9a0 100644 --- a/core/base/src/utils/mapping.ts +++ b/core/base/src/utils/mapping.ts @@ -63,22 +63,22 @@ //VR = value rows import type { - IndexEs, + RoTuple2D, + RoArray2D, + HeadTail, + Entries, Flatten, InnerFlatten, IsRectangular, - Zip, + TupleZip, Cartesian, - OnlyIndexes, - ExcludeIndexes, - ElementIndexPairs, + TuplePickWithOrder, } from './array.js'; import { range, zip } from './array.js'; -import type { Widen, RoArray, RoArray2D, RoPair } from './metaprogramming.js'; +import type { Widen, RoTuple, RoNeTuple, RoArray, RoPair } from './metaprogramming.js'; - -export type ShallowMapping> = - { readonly [E in M[number]as E[0]]: E[1] }; +export type ShallowMapping> = + { readonly [E in M[number] as E[0]]: E[1] }; //symbol probably shouldn't be part of the union (but then our type isn't a superset of PropertyKey // anymore which comes with its own set of headaches) @@ -87,11 +87,11 @@ function isMappableKey(key: unknown): key is MappableKey { return ["string", "number", "symbol", "bigint", "boolean"].includes(typeof key); } -export type MapLevel = RoArray>; +export type MapLevel = RoTuple>; -type MapLevelsTuple = readonly [MappableKey, ...RoArray, unknown]; +type MapLevelsTuple = readonly [MappableKey, ...RoTuple, unknown]; export type MapLevels = - T extends readonly [infer Head extends MappableKey, ...infer Tail extends RoArray] + T extends HeadTail ? Tail extends MapLevelsTuple ? MapLevel> : MapLevel @@ -114,7 +114,7 @@ type FromExtPropKey = : T; type MappingEntry = RoPair; -type MappingEntries = RoArray>; +type MappingEntries = RoTuple>; //Recursively sifts through T combining all row indexes that have key K. //Matching rows (i.e. those with key K) have their indexes placed in IA, non-matching (unfiltered) @@ -122,13 +122,10 @@ type MappingEntries = RoArray>; type CombineKeyRowIndexes< K extends MappableKey, T extends MappingEntries, - IA extends RoArray, //all values associated with K + IA extends RoTuple, //all values associated with K U extends MappingEntries = [], //rows that have been scanned and are not associated with K > = - T extends readonly [ - infer Head extends MappingEntry, - ...infer Tail extends MappingEntries - ] + T extends HeadTail ? Head[0] extends K ? CombineKeyRowIndexes : CombineKeyRowIndexes @@ -140,10 +137,10 @@ type CombineKeyRowIndexes< // [["Mainnet", 0], ["Mainnet", 1], ["Mainnet", 2], ["Testnet", 3], ["Testnet", 4]] //into [["Mainnet", [0,1,2]], ["Testnet", [3,4]]]. type ToMapEntries, M extends MappingEntries = []> = - KCI extends readonly [infer Head, ...infer Tail extends MappingEntries] + KCI extends HeadTail ? Head extends RoPair ? CombineKeyRowIndexes extends RoPair< - infer IA extends RoArray, + infer IA extends RoTuple, infer KCIU extends MappingEntries > ? ToMapEntries @@ -151,20 +148,39 @@ type ToMapEntries, M extends MappingEntries = : never : M; -type CartesianRightRecursive = - M extends MappingEntries - ? Flatten<[...{ [K in keyof M]: +type CartesianRightRecursive = + M extends infer IM extends MappingEntries + ? Flatten<[...{ [K in keyof IM]: K extends `${number}` - ? InnerFlatten>> + ? InnerFlatten>> : never }]> - : M extends MappingEntry - ? Cartesian + : M extends infer IM extends MappingEntry + ? Cartesian : M; -type Shape = RoPair; //key columns, value columns -type CartesianSet = RoArray2D; //CartesianSet is always rectangular -type Transpose = Zip; +type Shape = RoPair, RoTuple>; //key columns, value columns +type IndexLike = number | RoNeTuple; +type ShapeLike = RoPair; +type ShapeLikeToShape = + S extends RoPair + ? [KC extends number ? [KC] : KC, VC extends number ? [VC] : VC] + : never; + +type CartesianSet = RoTuple2D; //CartesianSet is always rectangular +type Transpose = + TupleZip extends infer R extends RoTuple2D + ? R + : never; + +type FlipInnerPair> = + [...{ [K in keyof T]: K extends `${number}` ? [T[K][1], T[K][0]] : never }]; + +type KeyRowIndexes = MappingEntries>; +type KeyColumnToKeyRowIndexes> = + FlipInnerPair> extends infer FIP extends MappingEntries + ? ToMapEntries + : never; //Takes the first of the reamining key columns and splits it into chunks that share the same key // value. Then invokes itself for each sub-chunk passing along only those value rows that belong @@ -172,23 +188,18 @@ type Transpose = Zip; //In our example for the default shape, it starts with the network column and splits it into // the "Mainnet" and "Testnet" chunk. The first chunk gets the first 3 rows, the second chunk // gets the last 2. Then the "Mainnet" chunk is recursively split into 3 chunks again, ... -type ProcessNextKeyColmn, VR extends RoArray> = - KC["length"] extends 0 - ? VR - : ExcludeIndexes extends infer KCR extends CartesianSet - //KRIA = key row indexes array - ? ToMapEntries> extends infer KRIA extends MappingEntries> - ? [...{ - [K in keyof KRIA]: [ - KRIA[K][0], +type ProcessNextKeyColmn, VR extends RoTuple> = + KC extends HeadTail + ? KeyColumnToKeyRowIndexes> extends infer KRI extends KeyRowIndexes + ? [...{ [K in keyof KRI]: [ + KRI[K][0], ProcessNextKeyColmn< - Transpose, KRIA[K][1]>>, - OnlyIndexes + Transpose, KRI[K][1]>, MappableKey>, + TuplePickWithOrder > - ] - }] + ]}] : never - : never; + : VR; //We encode leaf values as tuples of void (which does not constitute a value type and hence can't // come from the user) and the actual value. This allows us to later distinguish whether a value @@ -199,7 +210,7 @@ type LeafValue = RoPair; //Takes the value columns and combines them into leaf value rows. type CombineValueColumnsToLeafValues = //if we only have a single value column, we don't have to use tuples for values - (VC["length"] extends 1 ? VC[0] : Transpose) extends infer VCT extends RoArray + (VC["length"] extends 1 ? VC[0] : Transpose) extends infer VCT extends RoTuple ? [...{ [K in keyof VCT]: K extends `${number}` ? LeafValue : never }] : never; @@ -207,19 +218,19 @@ type CombineValueColumnsToLeafValues = // the specified shape. type SplitAndReorderKeyValueColumns = Transpose extends infer C extends CartesianSet - ? [OnlyIndexes, OnlyIndexes] + ? [TuplePickWithOrder, TuplePickWithOrder] : never; //returns the mapping with "unwrapped" values (i.e. turns the singleton arrays back into their one // constituent element) if all leaves are indeed singletons, otherwise returns void type UnwrapValuesIfAllAreSingletons = D extends 1 - ? M extends MappingEntries - ? [...{ [K in keyof M]: K extends `${number}` ? [M[K][0], M[K][1][0][1]] : never }] + ? M extends infer IM extends MappingEntries + ? [...{ [K in keyof IM]: K extends `${number}` ? [M[K][0], M[K][1][0][1]] : never }] : void - : M extends MappingEntries - ? [...{ [K in keyof M]: K extends `${number}` - ? [M[K][0], UnwrapValuesIfAllAreSingletons] + : M extends infer IM extends MappingEntries + ? [...{ [K in keyof IM]: K extends `${number}` + ? [IM[K][0], UnwrapValuesIfAllAreSingletons] : never }] extends infer U extends MappingEntries ? U @@ -232,28 +243,28 @@ type MaybeUnwrapValuesIfAllAreSingletons = //check that M has a valid structure for mapping entries - CartesianRightRecursive extends infer CRR extends RoArray2D + CartesianRightRecursive extends infer CRR extends RoTuple2D ? IsRectangular extends true //ensure CRR is not empty - ? CRR extends readonly [RoArray, ...RoArray2D] + ? CRR extends RoNeTuple ? S extends Shape ? SplitAndReorderKeyValueColumns extends [ infer KC extends CartesianSet, infer VC extends CartesianSet ] - ? KC["length"] extends Depth[number] - ? CombineValueColumnsToLeafValues extends infer VR extends RoArray + ? KC["length"] extends infer D extends Depth[number] + ? CombineValueColumnsToLeafValues extends infer VR extends RoTuple ? ProcessNextKeyColmn extends infer TM extends MappingEntries - ? [MaybeUnwrapValuesIfAllAreSingletons, KC["length"]] + ? [MaybeUnwrapValuesIfAllAreSingletons, D] : never : never : never : never //if we don't have an explicit shape, take the first row and subtract 1 (for the value // column) to determine the count of key columns - : CRR[0] extends readonly [...infer KC extends RoArray, unknown] - ? KC["length"] extends Depth[number] - ? [M, KC["length"]] + : CRR[0] extends readonly [...infer KC extends RoTuple, unknown] + ? KC["length"] extends infer D extends Depth[number] + ? [M, D] : never : never : never @@ -267,7 +278,7 @@ type ObjectFromMappingEntries ? D extends 1 ? V extends LeafValue ? T - : V extends RoArray + : V extends RoTuple ? [...{ [K2 in keyof V]: K2 extends `${number}` ? V[K2][1] : never }] : V : V extends MappingEntries @@ -279,9 +290,9 @@ type ObjectFromMappingEntries export type ToMappingAndDepth< M extends MappingEntries, - S extends Shape | void | undefined + S extends ShapeLike | undefined > = - TransformMapping extends [ + TransformMapping : void> extends [ infer TM extends MappingEntries, infer D extends Depth[number], ] @@ -290,13 +301,13 @@ export type ToMappingAndDepth< export type ToMapping< M extends MappingEntries, - S extends Shape | void | undefined = undefined + S extends ShapeLike | undefined = undefined > = ToMappingAndDepth[0]; type Mapped = { [key: PropertyKey]: unknown | Mapped }; -type RecursiveAccess> = - KA extends readonly [infer Head extends MappableKey, ...infer Tail extends RoArray] +type RecursiveAccess> = + KA extends HeadTail ? M extends Mapped ? RecursiveAccess], Tail> : never @@ -306,22 +317,22 @@ type RecursiveAccess> = // as to avoid having to hardcode arity...) type GenericMappingFunc = D extends 1 - ? >(...args: [K1]) => RecursiveAccess + ? >(...args: readonly [K1]) => RecursiveAccess : D extends 2 ? , const K2 extends FromExtPropKey>, - >(...args: [K1, K2]) => RecursiveAccess + >(...args: readonly [K1, K2]) => RecursiveAccess : D extends 3 ? , const K2 extends FromExtPropKey>, const K3 extends FromExtPropKey>, - >(...args: [K1, K2, K3]) => RecursiveAccess + >(...args: readonly [K1, K2, K3]) => RecursiveAccess : D extends 4 ? , const K2 extends FromExtPropKey>, const K3 extends FromExtPropKey>, const K4 extends FromExtPropKey>, - >(...args: [K1, K2, K3, K4]) => RecursiveAccess + >(...args: readonly [K1, K2, K3, K4]) => RecursiveAccess : never; type SubMap = @@ -350,7 +361,7 @@ type WidenedParamsAndRet = [Widen>>, ...WidenedParamsAndRetRec]; type HasGetFuncs = - WidenedParamsAndRet extends [...infer P extends RoArray, infer R] + WidenedParamsAndRet extends [...infer P extends RoTuple, infer R] ? { readonly has: (...args: P) => boolean, readonly get: (...args: P) => R | undefined } : never; @@ -359,7 +370,7 @@ type ConstMap = ? GenericMappingFunc & HasGetFuncs : GenericMappingFunc & HasGetFuncs & SubMapFunc; -type ToConstMap = +type ToConstMap = ToMappingAndDepth extends [infer TM extends Mapped, infer D extends Depth[number]] ? ConstMap : never; @@ -368,24 +379,24 @@ const isRecursiveTuple = (arr: RoArray) => arr.length === 2 && !Array.isArray(arr[0]) && Array.isArray(arr[1]); export const cartesianRightRecursive = - (arr: T): CartesianRightRecursive => ( + (arr: T): CartesianRightRecursive => ( arr.length === 0 ? [] : Array.isArray(arr[0]) ? (arr as MappingEntries).map(([key, val]) => Array.isArray(val) - ? (isRecursiveTuple(val) ? cartesianRightRecursive(val) : val) + ? (isRecursiveTuple(val) ? cartesianRightRecursive(val as unknown as RoTuple) : val) .map(ele => [key, ele].flat()) : [[key, val]] ).flat() : isRecursiveTuple(arr) - ? cartesianRightRecursive(arr[1] as RoArray).map((ele: any) => [arr[0], ele]) + ? cartesianRightRecursive(arr[1] as RoTuple).map((ele: any) => [arr[0], ele]) : arr ) as CartesianRightRecursive; const toMapping = < const M extends MappingEntries, - const S extends Shape | undefined = undefined + const S extends ShapeLike | undefined = undefined >(mapping: M, shape?: S): ToMapping => { const crr = cartesianRightRecursive(mapping); if (crr.length === 0) @@ -399,8 +410,8 @@ const toMapping = < let leafObjects = [] as any[]; let allSingletons = true; const buildMappingRecursively = ( - keyCartesianSet: CartesianSet, - values: RoArray + keyCartesianSet: MappableKey[][], + values: RoArray2D ): any => { const distinctKeys = Array.from(new Set(keyCartesianSet[0]).values()); const keyRows = new Map(distinctKeys.map(key => [key, []])); @@ -432,7 +443,7 @@ const toMapping = < const valuesSubset = rows.map(i => values[i]!); return [ key, - buildMappingRecursively(keyCartesianSubset as CartesianSet, valuesSubset) + buildMappingRecursively(keyCartesianSubset, valuesSubset) ]; })); }; @@ -460,10 +471,7 @@ const toMapping = < if (!isMappableKey(key)) throw new Error(`Invalid key: ${key} in ${keyCol}`); - const ret = buildMappingRecursively( - keyCartesianSet as CartesianSet, - zip(leafValues!) - ); + const ret = buildMappingRecursively(keyCartesianSet! as any, zip(leafValues!)); if (allSingletons) for (const leafObj of leafObjects) @@ -475,7 +483,7 @@ const toMapping = < export function constMap< const M extends MappingEntries, - const S extends Shape | undefined = undefined, + const S extends ShapeLike | undefined = undefined, >(mappingEntries: M, shape?: S): ToConstMap { const mapping = toMapping(mappingEntries, shape); const genericMappingFunc = ((...args: MappableKey[]) => @@ -484,10 +492,10 @@ export function constMap< mapping )); return Object.assign(genericMappingFunc, { - has: (...args: readonly MappableKey[]) => genericMappingFunc(...args) !== undefined, - get: (...args: readonly MappableKey[]) => genericMappingFunc(...args), + has: (...args: RoArray) => genericMappingFunc(...args) !== undefined, + get: (...args: RoArray) => genericMappingFunc(...args), subMap: (key: MappableKey) => (mapping as any)[key.toString()], - }) as ToConstMap; + }) as unknown as ToConstMap; } //--- find a bunch of "tests" below diff --git a/core/base/src/utils/metaprogramming.ts b/core/base/src/utils/metaprogramming.ts index d41eb2cab..7c3bf1a66 100644 --- a/core/base/src/utils/metaprogramming.ts +++ b/core/base/src/utils/metaprogramming.ts @@ -1,11 +1,19 @@ export type Extends = [T] extends [U] ? true : false; -//this is a generic overload of the built-in `Function` type, it should work as a more -// powerful drop-in replacement -export type Function

= (...args: P) => R; +export type NeTuple = [T, ...T[]]; +export type Tuple = NeTuple | []; +export type RoTuple = Readonly>; +export type RoNeTuple = Readonly>; export type RoArray = readonly T[]; -export type RoArray2D = RoArray>; export type RoPair = readonly [T, U]; +//Function is is a generic overload of the built-in type +// It should work as a more powerful drop-in replacement. +// Since the built-in type is not generic and permissive, we have to use RoArray as the +// default type of the parameters, otherwise `Test` would become false after our overload: +// type TestFunc = (...args: [string, number]) => boolean; +// type Test = TestFunc extends Function ? true : false; //true for built-in +export type Function

= RoArray, R = unknown> = + (...args: P) => R; export type Widen = T extends string ? string : @@ -22,15 +30,42 @@ export type IsAny = Extends<0, 1 & T>; export type IsNever = Extends; -//allow both And and And -export type And | boolean, R extends boolean = true> = +export type Not = B extends true ? false : true; + +//empty And is true (neutral element) +export type And | boolean, R extends boolean = true> = R extends true - ? T extends RoArray - ? Extends - : Extends + ? T extends RoTuple + ? false extends T[number] + ? false + : true + : T : false; -export type Not = B extends true ? false : true; +//empty And is false (neutral element) +export type Or | boolean, R extends boolean = false> = + R extends false + ? T extends RoTuple + ? true extends T[number] + ? true + : false + : T + : true; + +type XorImpl> = + T extends readonly [infer First, infer Second, ...infer Tail extends RoArray] + ? XorImpl<[boolean extends First | Second ? true : false, ...Tail]> + : T extends readonly [infer Final] + ? Final + : never; //Xor has no neutral element that we can return for the empty case + +//empty Xor is not supported (xor has no neutral element) +export type Xor | boolean, R extends boolean | undefined = undefined> = + T extends RoTuple + ? [...T, ...(undefined extends R ? [] : [Exclude])] extends infer V extends RoTuple + ? XorImpl + : never + : boolean extends Exclude | T ? true : false; export type ParseNumber = T extends `${infer N extends number}` ? N : never;