type-samurai
Version:
Advanced utility types for Typescript
132 lines (125 loc) • 3.73 kB
TypeScript
import { Decrement, _Decrement } from "./decrement";
import { LastCharacter } from "./last-character";
import { IsLowerThan } from "./lower-than";
import { Abs, IsNegativeInteger, Negate, ParseNumber } from "./number";
import { RemoveLeading } from "./remove-leading";
import { IfEmptyString, IsEmptyString } from "./string";
import { Stringify } from "./stringify";
import { Sum } from "./sum";
type SubDecrementMap = {
"-9": -10;
"-8": -9;
"-7": -8;
"-6": -7;
"-5": -6;
"-4": -5;
"-3": -4;
"-2": -3;
"-1": -2;
"0": -1;
"1": 0;
"2": 1;
"3": 2;
"4": 3;
"5": 4;
"6": 5;
"7": 6;
"8": 7;
"9": 8;
};
type SubNegativeCarryMap = {
"-10": 0;
"-9": 1;
"-8": 2;
"-7": 3;
"-6": 4;
"-5": 5;
"-4": 6;
"-3": 7;
"-2": 8;
"-1": 9;
};
type SubMap = {
0: [0, -1, -2, -3, -4, -5, -6, -7, -8, -9];
1: [1, 0, -1, -2, -3, -4, -5, -6, -7, -8];
2: [2, 1, 0, -1, -2, -3, -4, -5, -6, -7];
3: [3, 2, 1, 0, -1, -2, -3, -4, -5, -6];
4: [4, 3, 2, 1, 0, -1, -2, -3, -4, -5];
5: [5, 4, 3, 2, 1, 0, -1, -2, -3, -4];
6: [6, 5, 4, 3, 2, 1, 0, -1, -2, -3];
7: [7, 6, 5, 4, 3, 2, 1, 0, -1, -2];
8: [8, 7, 6, 5, 4, 3, 2, 1, 0, -1];
9: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
};
type _RemoveLeadingZeros<T extends string> = ParseNumber<
RemoveLeading<T, "0"> extends infer WithoutLeadingZeros extends string
? IfEmptyString<WithoutLeadingZeros, "0", WithoutLeadingZeros>
: never
>;
export type _Sub<
Num1 extends string,
Num2 extends string,
NegativeCarry extends 0 | 1 = 0,
Result extends string = ""
> = IsEmptyString<Num2> extends true
? NegativeCarry extends 0
? `${Num1}${Result}`
: `${Decrement<ParseNumber<Num1>>}${Result}`
: LastCharacter<Num1> extends `${infer Num1LastDigit extends keyof SubMap &
number}`
? LastCharacter<Num2> extends `${infer Num2LastDigit extends keyof SubMap[Num1LastDigit] &
number}`
? `${SubMap[Num1LastDigit][Num2LastDigit]}` extends infer DigitsSub extends keyof SubDecrementMap
? (
NegativeCarry extends 1
? Stringify<SubDecrementMap[DigitsSub]>
: DigitsSub
) extends infer DigitsSubWithCarry extends string
? Num1 extends `${infer Num1Rest}${Num1LastDigit}`
? Num2 extends `${infer Num2Rest}${Num2LastDigit}`
? DigitsSubWithCarry extends keyof SubNegativeCarryMap
? _Sub<
Num1Rest,
Num2Rest,
1,
`${SubNegativeCarryMap[DigitsSubWithCarry]}${Result}`
>
: _Sub<Num1Rest, Num2Rest, 0, `${DigitsSubWithCarry}${Result}`>
: never
: never
: never
: never
: never
: never;
/**
* Accepts two integers and returns their subtraction. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]`
* @example
* ```ts
* // 8
* type Case1 = Sub<10, 2>
* // -8
* type Case2 = Sub<2, 10>
* // 12
* type Case3 = Sub<2, -10>
* // -12
* type Case4 = Sub<-2, 10>
* ```
*/
export type Sub<
Num1 extends number,
Num2 extends number
> = IsNegativeInteger<Num1> extends true
? IsNegativeInteger<Num2> extends true
? IsLowerThan<Num1, Num2> extends true
? Negate<
_RemoveLeadingZeros<_Sub<Stringify<Abs<Num1>>, Stringify<Abs<Num2>>>>
>
: _RemoveLeadingZeros<_Sub<Stringify<Abs<Num2>>, Stringify<Abs<Num1>>>>
: Sum<Abs<Num1>, Num2> extends infer Result extends number
? Negate<Result>
: never
: IsNegativeInteger<Num2> extends true
? Sum<Num1, Abs<Num2>>
: IsLowerThan<Num1, Num2> extends true
? Negate<_RemoveLeadingZeros<_Sub<Stringify<Num2>, Stringify<Num1>>>>
: _RemoveLeadingZeros<_Sub<Stringify<Num1>, Stringify<Num2>>>;