Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion source/array-splice.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ The implementation of `SplitArrayByIndex` for variable length arrays.
type SplitVariableArrayByIndex<T extends UnknownArray,
SplitIndex extends number,
T1 = Subtract<SplitIndex, StaticPartOfArray<T>['length']>,
T2 = T1 extends number ? BuildTuple<T1, VariablePartOfArray<T>[number]> : [],
T2 = T1 extends number
? BuildTuple<GreaterThanOrEqual<T1, 0> extends true ? T1 : number, VariablePartOfArray<T>[number]>
: [],
> =
SplitIndex extends 0
? [[], T]
Expand Down
27 changes: 27 additions & 0 deletions source/internal/numeric.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {IsNever} from '../is-never';
import type {NegativeInfinity, PositiveInfinity} from '../numeric';
import type {UnknownArray} from '../unknown-array';
import type {StringToNumber} from './string';

Expand Down Expand Up @@ -89,3 +90,29 @@ type InternalUnionMax<N extends number, T extends UnknownArray = []> =
: T['length'] extends N
? InternalUnionMax<Exclude<N, T['length']>, T>
: InternalUnionMax<N, [...T, unknown]>;

/**
Returns the number with reversed sign.

@example
```
ReverseSign<-1>;
//=> 1

ReverseSign<1>;
//=> -1

ReverseSign<NegativeInfinity>
//=> PositiveInfinity

ReverseSign<PositiveInfinity>
//=> NegativeInfinity
```
*/
export type ReverseSign<N extends number> =
// Handle edge cases
N extends 0 ? 0 : N extends PositiveInfinity ? NegativeInfinity : N extends NegativeInfinity ? PositiveInfinity :
// Handle negative numbers
`${N}` extends `-${infer P extends number}` ? P
// Handle positive numbers
: `-${N}` extends `${infer R extends number}` ? R : never;
91 changes: 52 additions & 39 deletions source/subtract.d.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import type {NumberAbsolute, BuildTuple} from './internal';
import type {IsEqual} from './is-equal';
import type {NumberAbsolute, BuildTuple, ReverseSign} from './internal';
import type {PositiveInfinity, NegativeInfinity, IsNegative} from './numeric';
import type {LessThan} from './less-than';
import type {Sum} from './sum';
import type {And} from './and';
import type {Or} from './or';

/**
Returns the difference between two numbers.

Note:
- A or B can only support `-999` ~ `999`.
- If the result is negative, you can only get `number`.

@example
```
Expand All @@ -24,7 +19,10 @@ Subtract<111, -222>;
//=> 333

Subtract<-111, 222>;
//=> number
//=> -333

Subtract<18, 96>;
//=> -78

Subtract<PositiveInfinity, 9999>;
//=> PositiveInfinity
Expand All @@ -35,36 +33,51 @@ Subtract<PositiveInfinity, PositiveInfinity>;

@category Numeric
*/
// TODO: Support big integer and negative number.
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
? 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
// TODO: Support big integer.
export type Subtract<A extends number, B extends number> =
// Handle cases when A or B is the actual "number" type
number extends A | B ? number
// Handle cases when A and B are both +/- infinity
: A extends B & (PositiveInfinity | NegativeInfinity) ? number
// Handle cases when A is - infinity or B is + infinity
: A extends NegativeInfinity ? NegativeInfinity : B extends PositiveInfinity ? NegativeInfinity
// Handle cases when A is + infinity or B is - infinity
: A extends PositiveInfinity ? PositiveInfinity : B extends NegativeInfinity ? PositiveInfinity
// Handle case when numbers are equal to each other
: A extends B ? 0
// Handle cases when A or B is 0
: A extends 0 ? ReverseSign<B> : B extends 0 ? A
// Handle remaining regular cases
: SubtractPostChecks<A, B>;

/**
Subtracts two numbers A and B, such that they are not equal and neither of them are 0, +/- infinity or the `number` type
*/
type SubtractPostChecks<A extends number, B extends number, AreNegative = [IsNegative<A>, IsNegative<B>]> =
AreNegative extends [false, false]
? SubtractPositives<A, B>
: AreNegative extends [true, true]
// When both numbers are negative we subtract the absolute values and then reverse the sign
? ReverseSign<SubtractPositives<NumberAbsolute<A>, NumberAbsolute<B>>>
// When the signs are different we can add the absolute values and then reverse the sign if A < B
: [...BuildTuple<NumberAbsolute<A>>, ...BuildTuple<NumberAbsolute<B>>] extends infer R extends unknown[]
? LessThan<A, B> extends true ? ReverseSign<R['length']> : R['length']
: never;

/**
Subtracts two positive numbers.
*/
type SubtractPositives<A extends number, B extends number> =
LessThan<A, B> extends true
// When A < B we can reverse the result of B - A
? ReverseSign<SubtractIfAGreaterThanB<B, A>>
: SubtractIfAGreaterThanB<A, B>;

/**
Subtracts two positive numbers A and B such that A > B.
*/
type SubtractIfAGreaterThanB<A extends number, B extends number> =
// This is where we always want to end up and do the actual subtraction
BuildTuple<A> extends [...BuildTuple<B>, ...infer R]
? R['length']
: never;
53 changes: 35 additions & 18 deletions test-d/subtract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,43 @@ import {expectType} from 'tsd';
import type {Subtract} from '../index';
import type {NegativeInfinity, PositiveInfinity} from '../source/numeric';

expectType<Subtract<10, -2>>(12);
expectType<Subtract<2, 2>>(0);
expectType<Subtract<-1, -3>>(2);
// Positive result
expectType<12>({} as Subtract<10, -2>);
expectType<0>({} as Subtract<0, 0>);
expectType<0>({} as Subtract<2, 2>);
expectType<2>({} as Subtract<-1, -3>);
expectType<7>({} as Subtract<12, 5>);
expectType<69>({} as Subtract<69, 0>);
expectType<1000>({} as Subtract<999, -1>);
expectType<1998>({} as Subtract<999, -999>);

expectType<Subtract<1, 2>>(null! as number); // Note: you can only get `number` for now
// Negative result
expectType<-1>({} as Subtract<1, 2>);
expectType<-6>({} as Subtract<-10, -4>);
expectType<-14>({} as Subtract<-10, 4>);
expectType<-333>({} as Subtract<-111, 222>);
expectType<-420>({} as Subtract<0, 420>);
expectType<-1000>({} as Subtract<-999, 1>);
expectType<-1998>({} as Subtract<-999, 999>);

expectType<Subtract<PositiveInfinity, 999>>(null! as PositiveInfinity);
expectType<Subtract<-999, PositiveInfinity>>(null! as NegativeInfinity);
expectType<Subtract<NegativeInfinity, 999>>(null! as NegativeInfinity);
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);
// Infinity
expectType<PositiveInfinity>({} as Subtract<PositiveInfinity, 999>);
expectType<NegativeInfinity>({} as Subtract<-999, PositiveInfinity>);
expectType<NegativeInfinity>({} as Subtract<NegativeInfinity, 999>);
expectType<PositiveInfinity>({} as Subtract<999, NegativeInfinity>);
expectType<NegativeInfinity>({} as Subtract<NegativeInfinity, PositiveInfinity>);

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);
// Number
expectType<number>({} as Subtract<number, 2>);
expectType<number>({} as Subtract<2, number>);
expectType<number>({} as Subtract<number, number>);
expectType<number>({} as Subtract<NegativeInfinity, NegativeInfinity>);
expectType<number>({} as Subtract<PositiveInfinity, PositiveInfinity>);
expectType<number>({} as Subtract<number, PositiveInfinity>);
expectType<number>({} as Subtract<PositiveInfinity, number>);

// Union
expectType<Subtract<10, 1 | 2>>({} as 9 | 8);
expectType<Subtract<10 | 5, 1>>({} as 9 | 4);
expectType<Subtract<10 | 5, 1 | 2>>({} as 9 | 8 | 4 | 3);
expectType<9 | 8>({} as Subtract<10, 1 | 2>);
expectType<9 | 4>({} as Subtract<10 | 5, 1>);
expectType<9 | 8 | 4 | 3>({} as Subtract<10 | 5, 1 | 2>);
expectType<11 | 8 | -4 | -7>({} as Subtract<10 | -5, -1 | 2>);