Skip to content

Commit

Permalink
ArraySlice: Support array spread (#832)
Browse files Browse the repository at this point in the history
  • Loading branch information
Max10240 committed Mar 19, 2024
1 parent bc49577 commit d2d0d01
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 73 deletions.
45 changes: 29 additions & 16 deletions source/array-slice.d.ts
@@ -1,9 +1,11 @@
import type {Sum} from './sum';
import type {LessThan} from './less-than';
import type {LessThanOrEqual} from './less-than-or-equal';
import type {GreaterThanOrEqual} from './greater-than-or-equal';
import type {GreaterThan} from './greater-than';
import type {IsNegative} from './numeric';
import type {And} from './internal';
import type {And, Not, ArrayMin} from './internal';
import type {IsEqual} from './is-equal';
import type {ArraySplice} from './array-splice';

/**
Returns an array slice of a given range, just like `Array#slice()`.
Expand Down Expand Up @@ -54,9 +56,26 @@ slice[3];
*/
export type ArraySlice<
Array_ extends readonly unknown[],
Start extends number = 0,
End extends number = Array_['length'],
> = ArraySliceHelper<Array_, Start, End>;
Start extends number = never,
End extends number = never,
> = And<IsEqual<Start, never>, IsEqual<End, never>> extends true
? Array_
: number extends Array_['length']
? VariableLengthArraySliceHelper<Array_, Start, End>
: ArraySliceHelper<Array_, IsEqual<Start, never> extends true ? 0 : Start, IsEqual<End, never> extends true ? Array_['length'] : End>;

type VariableLengthArraySliceHelper<
Array_ extends readonly unknown[],
Start extends number,
End extends number,
> = And<Not<IsNegative<Start>>, IsEqual<End, never>> extends true
? ArraySplice<Array_, 0, Start>
: And<
And<Not<IsNegative<Start>>, Not<IsNegative<End>>>,
IsEqual<GreaterThan<End, Start>, true>
> extends true
? ArraySliceByPositiveIndex<Array_, Start, End>
: [];

type ArraySliceHelper<
Array_ extends readonly unknown[],
Expand All @@ -73,21 +92,15 @@ type ArraySliceHelper<
: never
: Start,
PositiveE extends number = IsNegative<End> extends true ? Sum<ArrayLength, End> : End,
> = true extends [IsNegative<PositiveS>, LessThanOrEqual<PositiveE, PositiveS>][number]
> = true extends [IsNegative<PositiveS>, LessThanOrEqual<PositiveE, PositiveS>, GreaterThanOrEqual<PositiveS, ArrayLength>][number]
? []
: ArraySliceByPositiveIndex<Array_, PositiveS, PositiveE>;
: ArraySliceByPositiveIndex<Array_, ArrayMin<[PositiveS, ArrayLength]>, ArrayMin<[PositiveE, ArrayLength]>>;

type ArraySliceByPositiveIndex<
Array_ extends readonly unknown[],
Start extends number,
End extends number,
TraversedElement extends Array<Array_[number]> = [],
Result extends Array<Array_[number]> = [],
> = Array_ extends readonly [infer H, ...infer Rest]
? And<
IsEqual<LessThanOrEqual<Start, TraversedElement['length']>, true>,
IsEqual<LessThan<TraversedElement['length'], End>, true>
> extends true
? ArraySliceByPositiveIndex<Rest, Start, End, [...TraversedElement, H], [...Result, H]>
: ArraySliceByPositiveIndex<Rest, Start, End, [...TraversedElement, H], Result>
: Result;
> = Start extends End
? Result
: ArraySliceByPositiveIndex<Array_, Sum<Start, 1>, End, [...Result, Array_[Start]]>;
60 changes: 31 additions & 29 deletions source/subtract.d.ts
Expand Up @@ -34,33 +34,35 @@ Subtract<PositiveInfinity, PositiveInfinity>;
@category Numeric
*/
// TODO: Support big integer and negative number.
export type Subtract<A extends number, B extends number> = [
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? Or<
And<IsEqual<R[0], true>, IsEqual<R[2], false>>,
And<IsEqual<R[3], true>, IsEqual<R[1], false>>
> extends true
? PositiveInfinity
: Or<
And<IsEqual<R[1], true>, IsEqual<R[3], false>>,
And<IsEqual<R[2], true>, IsEqual<R[0], false>>
export type Subtract<A extends number, B extends number> = number extends A | B
? number
: [
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? Or<
And<IsEqual<R[0], true>, IsEqual<R[2], false>>,
And<IsEqual<R[3], true>, IsEqual<R[1], false>>
> extends true
? NegativeInfinity
: true extends R[number]
? number
: [IsNegative<A>, IsNegative<B>] extends infer R
? [false, false] extends R
? BuildTuple<A> extends infer R
? R extends [...BuildTuple<B>, ...infer R]
? R['length']
: number
: never
: LessThan<A, B> extends true
? number
: [false, true] extends R
? Sum<A, NumberAbsolute<B>>
: Subtract<NumberAbsolute<B>, NumberAbsolute<A>>
: never
: never;
? PositiveInfinity
: Or<
And<IsEqual<R[1], true>, IsEqual<R[3], false>>,
And<IsEqual<R[2], true>, IsEqual<R[0], false>>
> extends true
? NegativeInfinity
: true extends R[number]
? number
: [IsNegative<A>, IsNegative<B>] extends infer R
? [false, false] extends R
? BuildTuple<A> extends infer R
? R extends [...BuildTuple<B>, ...infer R]
? R['length']
: number
: never
: LessThan<A, B> extends true
? number
: [false, true] extends R
? Sum<A, NumberAbsolute<B>>
: Subtract<NumberAbsolute<B>, NumberAbsolute<A>>
: never
: never;
58 changes: 30 additions & 28 deletions source/sum.d.ts
Expand Up @@ -34,33 +34,35 @@ Sum<PositiveInfinity, NegativeInfinity>;
@category Numeric
*/
// TODO: Support big integer and negative number.
export type Sum<A extends number, B extends number> = [
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? Or<
And<IsEqual<R[0], true>, IsEqual<R[3], false>>,
And<IsEqual<R[2], true>, IsEqual<R[1], false>>
> extends true
? PositiveInfinity
: Or<
And<IsEqual<R[1], true>, IsEqual<R[2], false>>,
And<IsEqual<R[3], true>, IsEqual<R[0], false>>
export type Sum<A extends number, B extends number> = number extends A | B
? number
: [
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? Or<
And<IsEqual<R[0], true>, IsEqual<R[3], false>>,
And<IsEqual<R[2], true>, IsEqual<R[1], false>>
> extends true
? NegativeInfinity
: true extends R[number]
? number
: ([IsNegative<A>, IsNegative<B>] extends infer R
? [false, false] extends R
? [...BuildTuple<A>, ...BuildTuple<B>]['length']
: [true, true] extends R
? number
: ArrayMax<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Max_
? ArrayMin<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Min_ extends number
? Max_ extends A | B
? Subtract<Max_, Min_>
: number
? PositiveInfinity
: Or<
And<IsEqual<R[1], true>, IsEqual<R[2], false>>,
And<IsEqual<R[3], true>, IsEqual<R[0], false>>
> extends true
? NegativeInfinity
: true extends R[number]
? number
: ([IsNegative<A>, IsNegative<B>] extends infer R
? [false, false] extends R
? [...BuildTuple<A>, ...BuildTuple<B>]['length']
: [true, true] extends R
? number
: ArrayMax<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Max_
? ArrayMin<[NumberAbsolute<A>, NumberAbsolute<B>]> extends infer Min_ extends number
? Max_ extends A | B
? Subtract<Max_, Min_>
: number
: never
: never
: never
: never) & number
: never;
: never) & number
: never;
18 changes: 18 additions & 0 deletions test-d/array-slice.ts
Expand Up @@ -18,3 +18,21 @@ expectType<ArraySlice<[0, 1, 2, 3], 0, -3>>([0]);
expectType<ArraySlice<[0, 1, 2, 3], 0, -4>>([]);
expectType<ArraySlice<[], -10, 1>>([]);
expectType<ArraySlice<[]>>([]);

expectType<ArraySlice<[...string[], 1, 2]>>([...(null! as string[]), 1, 2]);
expectType<ArraySlice<[...string[], 1, 2], 0>>([...(null! as string[]), 1, 2]);
expectType<ArraySlice<[...string[], 1, 2], 0, 2>>([null! as (string | 1 | 2), null! as (string | 1 | 2)]);
expectType<ArraySlice<[...string[], 1, 2], 1, 4>>([null! as (string | 1 | 2), null! as (string | 1 | 2), null! as (string | 1 | 2)]);

expectType<ArraySlice<[1, 2, 3, ...string[]]>>([1, 2, 3, ...(null! as string[])]);
expectType<ArraySlice<[1, 2, 3, ...string[]], 0>>([1, 2, 3, ...(null! as string[])]);
expectType<ArraySlice<[1, 2, 3, ...string[]], 0, 3>>([1, 2, 3]);
expectType<ArraySlice<[1, 2, 3, ...string[]], 1, 5>>([2, 3, null! as string, null! as string]);

expectType<ArraySlice<[1, 2, 3, ...string[], 4, 5]>>([1, 2, 3, ...(null! as string[]), 4, 5]);
expectType<ArraySlice<[1, 2, 3, ...string[], 4, 5], 0>>([1, 2, 3, ...(null! as string[]), 4, 5]);
expectType<ArraySlice<[1, 2, 3, ...string[], 4, 5], 1, 5>>([2, 3, null! as (string | 4 | 5), null! as (string | 4 | 5)]);
expectType<ArraySlice<[1, 2, 3, ...string[], 4, 5], 0>>([1, 2, 3, ...(null! as string[]), 4, 5]);
expectType<ArraySlice<[1, 2, 3, ...string[], 4, 5], 1>>([2, 3, ...(null! as string[]), 4, 5]);
expectType<ArraySlice<[1, 2, 3, ...string[], 4, 5], 3>>([...(null! as string[]), 4, 5]);
expectType<ArraySlice<[1, 2, 3, ...string[], 4, 5], 10>>([...(null! as string[]), 4, 5]);
5 changes: 5 additions & 0 deletions test-d/subtract.ts
Expand Up @@ -15,3 +15,8 @@ expectType<Subtract<999, NegativeInfinity>>(null! as PositiveInfinity);
expectType<Subtract<NegativeInfinity, PositiveInfinity>>(null! as NegativeInfinity);
expectType<Subtract<NegativeInfinity, NegativeInfinity>>(null! as number);
expectType<Subtract<PositiveInfinity, PositiveInfinity>>(null! as number);

expectType<Subtract<number, 2>>(null! as number);
expectType<Subtract<2, number>>(null! as number);
expectType<Subtract<number, number>>(null! as number);
expectType<Subtract<number, PositiveInfinity>>(null! as number);
5 changes: 5 additions & 0 deletions test-d/sum.ts
Expand Up @@ -13,3 +13,8 @@ expectType<Sum<-999, PositiveInfinity>>(null! as PositiveInfinity);
expectType<Sum<NegativeInfinity, 999>>(null! as NegativeInfinity);
expectType<Sum<999, NegativeInfinity>>(null! as NegativeInfinity);
expectType<Sum<NegativeInfinity, PositiveInfinity>>(null! as number);

expectType<Sum<number, 1>>(null! as number);
expectType<Sum<1, number>>(null! as number);
expectType<Sum<number, number>>(null! as number);
expectType<Sum<number, PositiveInfinity>>(null! as number);

0 comments on commit d2d0d01

Please sign in to comment.