ts-roids
Version:
Bullet-proof TS even more
2,113 lines (2,109 loc) • 79.7 kB
TypeScript
/**
* A type mapping interface that enables incremental type-level arithmetic operations.
* This interface maps each number from 0 to 32,767 to its successor (n + 1),
* enabling compile-time numeric operations in TypeScript's type system.
*
* @remarks
* This interface is primarily used as a helper for other type utilities that need
* to perform incremental operations, such as the `PositiveRange` type utility.
* The mapping is limited to numbers up to 32,768 due to TypeScript's computational limits.
*
* @example
* ```typescript
* type Next = NumberMap[5]; // Results in: 6
* type Invalid = NumberMap[32769]; // Results in: never
* ```
*
* @internal
* This interface is not intended to be used directly by consumers of the library.
*/
interface NumberMap {
0: 1;
1: 2;
2: 3;
3: 4;
4: 5;
5: 6;
6: 7;
7: 8;
8: 9;
9: 10;
10: 11;
11: 12;
12: 13;
13: 14;
14: 15;
15: 16;
16: 17;
17: 18;
18: 19;
19: 20;
20: 21;
21: 22;
22: 23;
23: 24;
24: 25;
25: 26;
26: 27;
27: 28;
28: 29;
29: 30;
30: 31;
31: 32;
32: 33;
33: 34;
34: 35;
35: 36;
36: 37;
37: 38;
38: 39;
39: 40;
40: 41;
41: 42;
42: 43;
43: 44;
44: 45;
45: 46;
46: 47;
47: 48;
48: 49;
49: 50;
50: 51;
51: 52;
52: 53;
53: 54;
54: 55;
55: 56;
56: 57;
57: 58;
58: 59;
59: 60;
60: 61;
61: 62;
62: 63;
63: 64;
64: 65;
65: 66;
66: 67;
67: 68;
68: 69;
69: 70;
70: 71;
71: 72;
72: 73;
73: 74;
74: 75;
75: 76;
76: 77;
77: 78;
78: 79;
79: 80;
80: 81;
81: 82;
82: 83;
83: 84;
84: 85;
85: 86;
86: 87;
87: 88;
88: 89;
89: 90;
90: 91;
91: 92;
92: 93;
93: 94;
94: 95;
95: 96;
96: 97;
97: 98;
98: 99;
99: 100;
100: 101;
101: 102;
102: 103;
103: 104;
104: 105;
105: 106;
106: 107;
107: 108;
108: 109;
109: 110;
110: 111;
111: 112;
112: 113;
113: 114;
114: 115;
115: 116;
116: 117;
117: 118;
118: 119;
119: 120;
120: 121;
121: 122;
122: 123;
123: 124;
124: 125;
125: 126;
126: 127;
127: 128;
128: 129;
129: 130;
130: 131;
131: 132;
132: 133;
133: 134;
134: 135;
135: 136;
136: 137;
137: 138;
138: 139;
139: 140;
140: 141;
141: 142;
142: 143;
143: 144;
144: 145;
145: 146;
146: 147;
147: 148;
148: 149;
149: 150;
150: 151;
151: 152;
152: 153;
153: 154;
154: 155;
155: 156;
156: 157;
157: 158;
158: 159;
159: 160;
160: 161;
161: 162;
162: 163;
163: 164;
164: 165;
165: 166;
166: 167;
167: 168;
168: 169;
169: 170;
170: 171;
171: 172;
172: 173;
173: 174;
174: 175;
175: 176;
176: 177;
177: 178;
178: 179;
179: 180;
180: 181;
181: 182;
182: 183;
183: 184;
184: 185;
185: 186;
186: 187;
187: 188;
188: 189;
189: 190;
190: 191;
191: 192;
192: 193;
193: 194;
194: 195;
195: 196;
196: 197;
197: 198;
198: 199;
199: 200;
200: 201;
201: 202;
202: 203;
203: 204;
204: 205;
205: 206;
206: 207;
207: 208;
208: 209;
209: 210;
210: 211;
211: 212;
212: 213;
213: 214;
214: 215;
215: 216;
216: 217;
217: 218;
218: 219;
219: 220;
220: 221;
221: 222;
222: 223;
223: 224;
224: 225;
225: 226;
226: 227;
227: 228;
228: 229;
229: 230;
230: 231;
231: 232;
232: 233;
233: 234;
234: 235;
235: 236;
236: 237;
237: 238;
238: 239;
239: 240;
240: 241;
241: 242;
242: 243;
243: 244;
244: 245;
245: 246;
246: 247;
247: 248;
248: 249;
249: 250;
250: 251;
251: 252;
252: 253;
253: 254;
254: 255;
255: 256;
256: 257;
257: 258;
258: 259;
259: 260;
260: 261;
261: 262;
262: 263;
263: 264;
264: 265;
265: 266;
266: 267;
267: 268;
268: 269;
269: 270;
270: 271;
271: 272;
272: 273;
273: 274;
274: 275;
275: 276;
276: 277;
277: 278;
278: 279;
279: 280;
280: 281;
281: 282;
282: 283;
283: 284;
284: 285;
285: 286;
286: 287;
287: 288;
288: 289;
289: 290;
290: 291;
291: 292;
292: 293;
293: 294;
294: 295;
295: 296;
296: 297;
297: 298;
298: 299;
299: 300;
300: 301;
301: 302;
302: 303;
303: 304;
304: 305;
305: 306;
306: 307;
307: 308;
308: 309;
309: 310;
310: 311;
311: 312;
312: 313;
313: 314;
314: 315;
315: 316;
316: 317;
317: 318;
318: 319;
319: 320;
320: 321;
321: 322;
322: 323;
323: 324;
324: 325;
325: 326;
326: 327;
327: 328;
328: 329;
329: 330;
330: 331;
331: 332;
332: 333;
333: 334;
334: 335;
335: 336;
336: 337;
337: 338;
338: 339;
339: 340;
340: 341;
341: 342;
342: 343;
343: 344;
344: 345;
345: 346;
346: 347;
347: 348;
348: 349;
349: 350;
350: 351;
351: 352;
352: 353;
353: 354;
354: 355;
355: 356;
356: 357;
357: 358;
358: 359;
359: 360;
360: 361;
361: 362;
362: 363;
363: 364;
364: 365;
365: 366;
366: 367;
367: 368;
368: 369;
369: 370;
370: 371;
371: 372;
372: 373;
373: 374;
374: 375;
375: 376;
376: 377;
377: 378;
378: 379;
379: 380;
380: 381;
381: 382;
382: 383;
383: 384;
384: 385;
385: 386;
386: 387;
387: 388;
388: 389;
389: 390;
390: 391;
391: 392;
392: 393;
393: 394;
394: 395;
395: 396;
396: 397;
397: 398;
398: 399;
399: 400;
400: 401;
401: 402;
402: 403;
403: 404;
404: 405;
405: 406;
406: 407;
407: 408;
408: 409;
409: 410;
410: 411;
411: 412;
412: 413;
413: 414;
414: 415;
415: 416;
416: 417;
417: 418;
418: 419;
419: 420;
420: 421;
421: 422;
422: 423;
423: 424;
424: 425;
425: 426;
426: 427;
427: 428;
428: 429;
429: 430;
430: 431;
431: 432;
432: 433;
433: 434;
434: 435;
435: 436;
436: 437;
437: 438;
438: 439;
439: 440;
440: 441;
441: 442;
442: 443;
443: 444;
444: 445;
445: 446;
446: 447;
447: 448;
448: 449;
449: 450;
450: 451;
451: 452;
452: 453;
453: 454;
454: 455;
455: 456;
456: 457;
457: 458;
458: 459;
459: 460;
460: 461;
461: 462;
462: 463;
463: 464;
464: 465;
465: 466;
466: 467;
467: 468;
468: 469;
469: 470;
470: 471;
471: 472;
472: 473;
473: 474;
474: 475;
475: 476;
476: 477;
477: 478;
478: 479;
479: 480;
480: 481;
481: 482;
482: 483;
483: 484;
484: 485;
485: 486;
486: 487;
487: 488;
488: 489;
489: 490;
490: 491;
491: 492;
492: 493;
493: 494;
494: 495;
495: 496;
496: 497;
497: 498;
498: 499;
499: 500;
500: 501;
501: 502;
502: 503;
503: 504;
504: 505;
505: 506;
506: 507;
507: 508;
508: 509;
509: 510;
510: 511;
511: 512;
512: 513;
513: 514;
514: 515;
515: 516;
516: 517;
517: 518;
518: 519;
519: 520;
520: 521;
521: 522;
522: 523;
523: 524;
524: 525;
525: 526;
526: 527;
527: 528;
528: 529;
529: 530;
530: 531;
531: 532;
532: 533;
533: 534;
534: 535;
535: 536;
536: 537;
537: 538;
538: 539;
539: 540;
540: 541;
541: 542;
542: 543;
543: 544;
544: 545;
545: 546;
546: 547;
547: 548;
548: 549;
549: 550;
550: 551;
551: 552;
552: 553;
553: 554;
554: 555;
555: 556;
556: 557;
557: 558;
558: 559;
559: 560;
560: 561;
561: 562;
562: 563;
563: 564;
564: 565;
565: 566;
566: 567;
567: 568;
568: 569;
569: 570;
570: 571;
571: 572;
572: 573;
573: 574;
574: 575;
575: 576;
576: 577;
577: 578;
578: 579;
579: 580;
580: 581;
581: 582;
582: 583;
583: 584;
584: 585;
585: 586;
586: 587;
587: 588;
588: 589;
589: 590;
590: 591;
591: 592;
592: 593;
593: 594;
594: 595;
595: 596;
596: 597;
597: 598;
598: 599;
599: 600;
600: 601;
601: 602;
602: 603;
603: 604;
604: 605;
605: 606;
606: 607;
607: 608;
608: 609;
609: 610;
610: 611;
611: 612;
612: 613;
613: 614;
614: 615;
615: 616;
616: 617;
617: 618;
618: 619;
619: 620;
620: 621;
621: 622;
622: 623;
623: 624;
624: 625;
625: 626;
626: 627;
627: 628;
628: 629;
629: 630;
630: 631;
631: 632;
632: 633;
633: 634;
634: 635;
635: 636;
636: 637;
637: 638;
638: 639;
639: 640;
640: 641;
641: 642;
642: 643;
643: 644;
644: 645;
645: 646;
646: 647;
647: 648;
648: 649;
649: 650;
650: 651;
651: 652;
652: 653;
653: 654;
654: 655;
655: 656;
656: 657;
657: 658;
658: 659;
659: 660;
660: 661;
661: 662;
662: 663;
663: 664;
664: 665;
665: 666;
666: 667;
667: 668;
668: 669;
669: 670;
670: 671;
671: 672;
672: 673;
673: 674;
674: 675;
675: 676;
676: 677;
677: 678;
678: 679;
679: 680;
680: 681;
681: 682;
682: 683;
683: 684;
684: 685;
685: 686;
686: 687;
687: 688;
688: 689;
689: 690;
690: 691;
691: 692;
692: 693;
693: 694;
694: 695;
695: 696;
696: 697;
697: 698;
698: 699;
699: 700;
700: 701;
701: 702;
702: 703;
703: 704;
704: 705;
705: 706;
706: 707;
707: 708;
708: 709;
709: 710;
710: 711;
711: 712;
712: 713;
713: 714;
714: 715;
715: 716;
716: 717;
717: 718;
718: 719;
719: 720;
720: 721;
721: 722;
722: 723;
723: 724;
724: 725;
725: 726;
726: 727;
727: 728;
728: 729;
729: 730;
730: 731;
731: 732;
732: 733;
733: 734;
734: 735;
735: 736;
736: 737;
737: 738;
738: 739;
739: 740;
740: 741;
741: 742;
742: 743;
743: 744;
744: 745;
745: 746;
746: 747;
747: 748;
748: 749;
749: 750;
750: 751;
751: 752;
752: 753;
753: 754;
754: 755;
755: 756;
756: 757;
757: 758;
758: 759;
759: 760;
760: 761;
761: 762;
762: 763;
763: 764;
764: 765;
765: 766;
766: 767;
767: 768;
768: 769;
769: 770;
770: 771;
771: 772;
772: 773;
773: 774;
774: 775;
775: 776;
776: 777;
777: 778;
778: 779;
779: 780;
780: 781;
781: 782;
782: 783;
783: 784;
784: 785;
785: 786;
786: 787;
787: 788;
788: 789;
789: 790;
790: 791;
791: 792;
792: 793;
793: 794;
794: 795;
795: 796;
796: 797;
797: 798;
798: 799;
799: 800;
800: 801;
801: 802;
802: 803;
803: 804;
804: 805;
805: 806;
806: 807;
807: 808;
808: 809;
809: 810;
810: 811;
811: 812;
812: 813;
813: 814;
814: 815;
815: 816;
816: 817;
817: 818;
818: 819;
819: 820;
820: 821;
821: 822;
822: 823;
823: 824;
824: 825;
825: 826;
826: 827;
827: 828;
828: 829;
829: 830;
830: 831;
831: 832;
832: 833;
833: 834;
834: 835;
835: 836;
836: 837;
837: 838;
838: 839;
839: 840;
840: 841;
841: 842;
842: 843;
843: 844;
844: 845;
845: 846;
846: 847;
847: 848;
848: 849;
849: 850;
850: 851;
851: 852;
852: 853;
853: 854;
854: 855;
855: 856;
856: 857;
857: 858;
858: 859;
859: 860;
860: 861;
861: 862;
862: 863;
863: 864;
864: 865;
865: 866;
866: 867;
867: 868;
868: 869;
869: 870;
870: 871;
871: 872;
872: 873;
873: 874;
874: 875;
875: 876;
876: 877;
877: 878;
878: 879;
879: 880;
880: 881;
881: 882;
882: 883;
883: 884;
884: 885;
885: 886;
886: 887;
887: 888;
888: 889;
889: 890;
890: 891;
891: 892;
892: 893;
893: 894;
894: 895;
895: 896;
896: 897;
897: 898;
898: 899;
899: 900;
900: 901;
901: 902;
902: 903;
903: 904;
904: 905;
905: 906;
906: 907;
907: 908;
908: 909;
909: 910;
910: 911;
911: 912;
912: 913;
913: 914;
914: 915;
915: 916;
916: 917;
917: 918;
918: 919;
919: 920;
920: 921;
921: 922;
922: 923;
923: 924;
924: 925;
925: 926;
926: 927;
927: 928;
928: 929;
929: 930;
930: 931;
931: 932;
932: 933;
933: 934;
934: 935;
935: 936;
936: 937;
937: 938;
938: 939;
939: 940;
940: 941;
941: 942;
942: 943;
943: 944;
944: 945;
945: 946;
946: 947;
947: 948;
948: 949;
949: 950;
950: 951;
951: 952;
952: 953;
953: 954;
954: 955;
955: 956;
956: 957;
957: 958;
958: 959;
959: 960;
960: 961;
961: 962;
962: 963;
963: 964;
964: 965;
965: 966;
966: 967;
967: 968;
968: 969;
969: 970;
970: 971;
971: 972;
972: 973;
973: 974;
974: 975;
975: 976;
976: 977;
977: 978;
978: 979;
979: 980;
980: 981;
981: 982;
982: 983;
983: 984;
984: 985;
985: 986;
986: 987;
987: 988;
988: 989;
989: 990;
990: 991;
991: 992;
992: 993;
993: 994;
994: 995;
995: 996;
996: 997;
997: 998;
998: 999;
}
/**
* Used to display an error message instead of never, for better readability
* @export type {UnassignableTypeError}
*/
type UnassignableTypeError<ErrType extends string> = `type is not assignable to type ${ErrType}`;
/**
*
* Represents a type that can either be ``null`` or ``undefined``.
* @export type {Nullable}
*/
type Nullable = null | undefined;
/**
* Represents a type that can hold any numeric value: number or a bigint.
* @export type {Numeric}
*/
type Numeric = number | bigint;
/**
* Represents all the primitive types in JavaScript.
* - `Nullable`: A value that can be either null or undefined.
* - `Numeric`: A value that can be either a number or a bigint.
* - `string`: Represents textual data.
* - `boolean`: Represents a logical value (true or false).
* - `symbol`: Represents a unique and immutable value.
*/
type Primitive = string | boolean | symbol | Nullable | Numeric;
/**
* Represents a type that includes falsy values in JavaScript.
* Falsy values are those that coerce to false when used in a boolean context.
* This includes `false`, an empty string (`''`), numeric zero (`0`), `null`,
* and `undefined`.
*/
type Falsy = false | '' | 0 | Nullable;
/**
* This type is used to describe constructor functions or classes
* that can be invoked using the `new` keyword.
*/
type Newable = {
new (...args: any[]): any;
};
/**
* Describes any function accepting any arguments
* and returning any value.
*/
type AnyFunction = (...args: any[]) => any;
/**
* Describes any function accepting and retruning `unknown`s
*/
type UnknownFunction = (...args: unknown[]) => unknown;
/**
* Represents an integer type.
* This type is used to ensure that a numeric value is an integer.
*
* Example use case:
*
* ```ts
* export function myFunc<T extends Numeric>(a: Integer<T>) {
* console.log(a);
* }
* const good = myFunc(4545); // This is valid as 4545 is an integer.
* const bad = myFunc(4545.554); // This will throw an error as 4545.554 is not an integer.
* ```
*/
type Integer<N extends Numeric> = IfEquals<IsInteger<N>, true, N, never>;
/**
* Type representing an integer that's in [0,+∞[
*/
type PositiveInteger<N extends Numeric> = IfEquals<IsPositiveInteger<N>, true, Integer<N>, never>;
/**
* Represents a positive integer parsed from a string.
* If the string does not represent a positive integer, it resolves to `never`, else
* it resolves to its integer representation.
* @example
* ````ts
PositiveIntegerString<'0'>; // works
PositiveIntegerString<'82739283293237'>; // works
PositiveIntegerString<'82739.283293237'>; // never
PositiveIntegerString<'-82739.283293237'>; // never
PositiveIntegerString<'-1'>; // never
* ````
*/
type PositiveIntegerString<S extends string> = IfEquals<IsPositiveInteger<Integer<NumerifyString<S>>>, true, Integer<NumerifyString<S>>, never>;
/**
* Type representing an integer that's in ]-∞, 0[
*/
type NegativeInteger<N extends Numeric> = IfEquals<IsNegativeInteger<N>, true, Integer<N>, never>;
/**
* Represents a negative integer parsed from a string.
* If the string does not represent a negative integer, it resolves to `never`, else
* it resolves to its integer representation.
* @example
* ````ts
NegativeIntegerString<'0'>; // never
NegativeIntegerString<'82739283293237'>; // never
NegativeIntegerString<'-82739.283293237'>; // works
NegativeIntegerString<'-82739.283293237'>; // never
NegativeIntegerString<'-1'>; // works
* ````
*/
type NegativeIntegerString<S extends string> = IfEquals<IsNegativeInteger<Integer<NumerifyString<S>>>, true, Integer<NumerifyString<S>>, never>;
/**
* Is it a negative integer ?
* @return
* `true` if it is, else `false`
*/
type IsNegativeInteger<N extends Numeric> = IsNegative<Integer<N>>;
/**
* Is it a positive integer ?
* @return
* `true` if it is, else `false`
*/
type IsPositiveInteger<N extends Numeric> = IsPositive<Integer<N>>;
/**
* Type representing a float
*/
type Float<N extends Numeric> = IfExtends<IsFloat<N>, true, N, never>;
/**
* Type representing a float that's in [0,+∞[
*/
type PositiveFloat<N extends Numeric> = IfEquals<IsPositiveFloat<N>, true, Float<N>, never>;
/**
* Represents a positive float parsed from a string.
* If the string does not represent a positive float, it resolves to `never`, else
* it resolves to its float representation.
* @example
* ````ts
PositiveFloatString<'0'>; // never
PositiveFloatString<'82739283293237'>; // works
PositiveFloatString<'-82739.283293237'>; // never
PositiveFloatString<'-1'>; // never
PositiveFloatString<'1.98'>; // works
PositiveFloatString<'-1.98'>; // never
* ````
*/
type PositiveFloatString<S extends string> = IfEquals<IsPositiveFloat<Float<NumerifyString<S>>>, true, Float<NumerifyString<S>>, never>;
/**
* Type representing a float that's in ]-∞, 0[
*/
type NegativeFloat<N extends Numeric> = IfEquals<IsNegativeFloat<N>, true, Float<N>, never>;
/**
* Represents a negative float parsed from a string.
* If the string does not represent a negative float, it resolves to `never`, else
* it resolves to its float representation.
* @example
* ````ts
NegativeFloatString<'0'>; // never
NegativeFloatString<'82739283293237'>; // never
NegativeFloatString<'-82739.283293237'>; // works
NegativeFloatString<'-1'>; // never
NegativeFloatString<'-1.98'>; // works
* ````
*/
type NegativeFloatString<S extends string> = IfEquals<IsNegativeFloat<Float<NumerifyString<S>>>, true, Float<NumerifyString<S>>, never>;
/**
* Is it a negative float ?
* @return
* `true` if it is, else `false`
*/
type IsNegativeFloat<N extends Numeric> = IsNegative<Float<N>>;
/**
* Is it a positive float ?
* @return
* `true` if it is, else `false`
*/
type IsPositiveFloat<N extends Numeric> = IsPositive<Float<N>>;
/**
* Represents an odd numeric?
* @example
* ````ts
* Odd<2587967>; // 2587967
* Odd<215848141>; // 215848141
* Odd<200000000000000>; // never
* Odd<200000000000000.55>; // never
* Odd<200000000000001.53>; // never
* ````
*/
type Odd<T extends Numeric> = IfExtends<StringifyPrimitive<Integer<T>>, `${Numeric | ''}${1 | 3 | 5 | 7 | 9}`, T, never>;
/**
* Represents an even numeric
* @example
* ````ts
* Even<200000000000000>; // 258796
* Even<258796>; // 258796
* Even<2000000000000001>; // never
* ````
*/
type Even<T extends Numeric> = IfExtends<StringifyPrimitive<Integer<T>>, `${Numeric | ''}${2 | 4 | 6 | 8 | 0}`, T, never>;
/**
* `Optional<T>` is similar to Python's `Optional` and Rust's `Option` types.
* It promotes more predictable code,
* by enforcing explicit handling of optional scenarios, e.g: requiring functions
* to return `null` specifically when a value is absent.
*/
type Optional<T> = T | null;
/**
Represnets a type that might be nullable, as in it might be `null` or `undefined`.
*/
type Maybe<T> = T | Nullable;
type MaybeUnknown<T> = T | unknown;
type MaybeUndefined<T> = T | undefined;
/**
* Presents any non-nullish value
*/
type EmptyObject = NonNullable<unknown>;
/**
* Extracts truthy properties from an object type `T`.
* @example
* ````ts
type T = {
name: string;
age: number;
hasCar: boolean;
address?: string | null;
}
type R = TruthyProperties<T>;
// Result: { name: string; age: number; hasCar: boolean; }
* ````
*/
type TruthyProperties<T> = Pick<T, {
[K in Keys<T>]: IsTruthy<T[K]> extends true ? K : never;
}[Keys<T>]>;
/**
* Extracts falsy properties from an object type `T`.
* @example
* ````ts
type T = {
a: string;
b: number;
c: boolean;
d?: string | null;
e: 0;
f: null;
};
type az = FalsyProperties<T>;
// Result: {
e: 0;
f: null;
}
* ````
*/
type FalsyProperties<T> = Pick<T, {
[K in Keys<T>]: IsFalsy<T[K]> extends true ? K : never;
}[Keys<T>]>;
/**
* Checks if a given type `T` is `Falsy`.
* @returns `true` if `T` is a subtype of `Falsy`, otherwise `false`.
* @example
* type Falsy = IsFalsy<''>; // TestFalsy is tru`
* type Truthy = IsFalsy<10>; // TestTruthy is false
*/
type IsFalsy<T> = IfExtends<T, Falsy, true, false>;
/**
* Checks if a given type `T` is a truthy value.
* A truthy value is any value that is not a falsy value.
* @returns `true` if `T` is not a subtype of `Falsy`, otherwise `false`.
* @example
* type TruthyString = IsTruthy<string>; // => true
* type TruthyNumber = IsTruthy<10>; // => true
* type FalsyNull = IsTruthy<null>; // => false
* type FalsyEmptyString = IsTruthy<''>; => false
*/
type IsTruthy<T> = IfExtends<T, Exclude<T, Falsy>, true, false>;
/**
* Checks if a given type `T` is `never`.
* The `never` type represents a value that never occurs,
* for example a function that always errors out.
* @returns `true` if `T` is `never`, otherwise `false`.
* @example
* type Never = IsNever<never>; => true
* type NotNever = IsNever<string>; => false
*/
type IsNever<T> = Equals<T, never>;
/**
* A nullable type is a type that might be null, undefined or both
* @returns
* `true` if it is, else `false`
*/
type IsNullable<T> = IfExtends<T, Nullable, true, false>;
/**
* Checks if a given type `T` is `unknown`.
*
* Values of type `unknown` can hold any value, similar to `any`, but with stricter type safety.
* Unlike `any`, you cannot perform operations directly on values of type `unknown`
* without type assertion or type narrowing.
* @returns `true` if `T` is `unknown`, otherwise `false`.
* @example
* type IsUnknownValue = IsUnknown<unknown>; // true
* type IsNotUnknownValue = IsUnknown<string>; // also true
* @remarks
* > If you want `unknown` to be exact, use `IsExactlyUnknown`
*/
type IsUnknown<T> = IfExtends<T, unknown, true, false>;
/**
* Checks if a given type `T` is exactly `unknown`.
* @example
* ````ts
IsExactlyUnknown<any | unknown>; // false, since the union evaluates to any
IsExactlyAny<any | unknown>; // true
IsExactlyUnknown<unknown | string> // true
IsExactlyUnknown<string>; // flase
* ````
*/
type IsExactlyUnknown<T> = Equals<T, unknown>;
/**
* @returns `true` if `T` is `string`, otherwise `false`.
*/
type IsString<T> = IfExtends<T, string, true, false>;
/**
* @returns `true` if `T` is excatly `string`, otherwise `false`.
* @example
* ````ts
IsExactlyString<number>; // false;
IsExactlyString<any | string>; // false
IsExactlyString<unknown | string>; // false
* ````
*/
type IsExactlyString<T> = Equals<T, string>;
/**
* @returns `true` if `T` is `boolean`, otherwise `false`.
*/
type IsBoolean<T> = IfExtends<T, boolean, true, false>;
/**
* A numeric type iincludes `number` and `bigint`.
* @returns `true` if `T` is a numeric type, otherwise `false`.
*/
type IsNumeric<T> = IfExtends<T, Numeric, true, false>;
/**
* Represents a tuple.
* @example
* ````ts
* Tuple<['a', 'b']>; // Result: ['a', 'b']
* Tuple<string[]>; // Result: never
*/
type Tuple<T> = T extends readonly unknown[] ? number extends T['length'] ? never : T : never;
/**
* Represents a tuple of size `N`, where `N` is a positive integer.
* The tuple's length is exactly `N`, with each element of the tuple being of type `T`.
*
* @template T The type of the elements in the tuple.
* @template N The desired length of the tuple.
* @template Acc Accumulator type for recursive construction.
*
* @example
* ```
* SizedTuple<string, 3>; // Result: [string, string, string]
* SizedTuple<number, 2>; // Result: [number, number]
* SizedTuple<number, 0>; // Result: []
* ```
*
* @see Tuple
*/
type SizedTuple<T, N extends Numeric, Acc extends T[] = []> = N extends PositiveInteger<infer M> ? Acc['length'] extends M ? Acc : SizedTuple<T, M, [T, ...Acc]> : never;
/**
* Is a given type `T` an array?
* @returns `true` if `T` it is, otherwise `false`.
* @example
* ```
* IsArray<number[]>; // true
* IsArray<string>; // false
* ```
*/
type IsArray<T> = IfExtends<T, unknown[], true, false>;
/**
* Represents a unique array type that filters out duplicate elements.
* If a duplicate is encountered, it returns a tuple with an error message and the duplicate element.
*
* @template T The type of the elements in the array, which must be a readonly array.
* @returns A readonly array of unique elements or a tuple indicating a duplicate.
*
* @example
* ```
* type Result1 = UniqueArray<[1, 2, 3, 1]>; // Result: ['Encountered duplicate element', 1]
* type Result2 = UniqueArray<[1, 2, 3]>; // Result: [1, 2, 3]
* type Result3 = UniqueArray<[]>; // Result: []
* ```
*/
type UniqueArray<T extends readonly any[], Seen = never> = T extends readonly [infer Head, ...infer Tail] ? Head extends Seen ? ['Encountered duplicate element', Head] : readonly [Head, ...UniqueArray<Tail, Seen | Head>] : T;
/**
* @returns `true` if `Arr` is an array that includes elements of type `T`, otherwise `false`.
* An array of elements of type `T` is defined as `Arr` being a subtype of `T[]`.
* @example
* ```
* IsArrayIncludeTypeof<Record<string, number>[], number> // false
* IsArrayIncludeTypeof<string[], null | boolean> // false
* ArrayIncludeTypeof<string[], string | boolean> // true
* IsArrayIncludeTypeof<Record<string, number>[], Record<symbol, boolean>> // true,
* ```
*/
type IsArrayIncludesTypeof<Arr, T> = IfExtends<Arr, T[], true, false>;
/**
* Type utility that checks if a given type `T` is an `AnyFunction` (any function type).
* @template T The type to check.
* @returns `true` if `T` is an `AnyFunction`, otherwise `false`.
* An `AnyFunction` is defined as a function type that accepts any arguments and returns any value.
* @example
* ```
* IsAnyFunction<() => void>; // true (matches AnyFunction)
* IsAnyFunction<(x: number) => string>; // true (matches AnyFunction)
* IsAnyFunction<string>; // false (string is not a function type)
* ```
*/
type IsAnyFunction<T> = IfExtends<T, AnyFunction, true, false>;
/**
* Type utility that checks if a given type `T` is a `Function` (function type accepting `unknown` arguments and returning `unknown`).
* @template T The type to check.
* @returns `true` if `T` is a `Function`, otherwise `false`.
* A `Function` is defined as a function type that accepts arguments of type `unknown` and returns a value of type `unknown`.
* @example
* ```
* IsFunction<() => void>; // true (matches Function)
* IsFunction<(x: number) => string>; // true (matches Function)
* IsFunction<string>; // false (string is not a function type)
* ```
*/
type IsFunction<T> = IfExtends<T, UnknownFunction, true, false>;
/**
* Checks if a given type `T` qualifies as an object.
* @returns `true` if it is, otherwise `false`.
* An object in this context is defined as a non-null object (excluding functions and arrays).
* @example
* ```
IsObject<object>; // true
IsObject<{ name: string }>; // true
IsObject<string>; // false
IsObject<Function>; // true, yes, the built-in Function type is an interface with a bunch of methods, so yes it's an object.
// if you want to use the function type use this:
IsObject<UnknownFunction>; // false
// or this
IsObject<AnyFunction>; // false
IsObject<any[]>; // false
IsObject<null>; // false
* ```
*/
type IsObject<T> = And<IfExtends<T, object, true, false>, And<Not<IsFunction<T>>, Not<IsArray<T>>>>;
/**
* @returns `true` if `T` is `number`, otherwise `false`.
*/
type IsNumber<T> = IfExtends<T, number, true, false>;
/**
* @returns `true` if `T` is exactly of type `number`, otherwise `false`.
* @example
* ````ts
IsExactlyNumber<any | number>; // false
IsExactlyNumber<unknown | number> // false
IsExactlyNumber<number> // true
IsExactlyNumber<87> // false
* ````
*/
type IsExactlyNumber<T> = Equals<T, number>;
/**
* @returns `true` if `T` is `bigint`, otherwise `false`.
*/
type IsBigInt<T> = IfExtends<T, bigint, true, false>;
/**
* @returns `true` if `T` is exactly `bigint`, otherwise `false`.
* @example
* ````ts
IsBigInt<unknown | bigint>; // false
IsBigInt<symbol | Nullable>; // false
IsBigInt<bigint>; // true
* ````
*/
type IsExactlyBigInt<T> = Equals<T, bigint>;
/**
* @returns `true` if `T` is `symbol`, otherwise `false`.
*/
type IsSymbol<T> = T extends symbol ? true : false;
/**
* @returns `true` if `T` is exactly `symbol`, otherwise `false`.
* @example
* ````ts
IsExactlySymbol<symbol>; // true
IsExactlySymbol<any | symbol>; // false
IsExactlySymbol<Integer<2>>; // false
* ````
*/
type IsExactlySymbol<T> = Equals<T, symbol>;
/**
* If ``T`` is exactly``any``, return ``true``, otherwise, return ``false``.
* @example
* ````ts
IsExactlyAny<any | Nullable>; // true, once unsafe, always unsafe
IsExactlyAny<Numeric | Nullable>; // false
IsExactlyAny<unknown>; // false
IsExactlyAny<any>, true
* ````
*/
type IsExactlyAny<T> = Equals<T, any>;
/**
* Checks if a given numeric value is in ]-∞,0[
* @returns
* true if it is, otherwise false
*/
type IsNegative<N extends Numeric> = StringifyPrimitive<N> extends `-${infer U}` ? true : false;
/**
* Checks if a given numeric value is in [0,+∞[
* @returns
* true if it is, otherwise false
*/
type IsPositive<N extends Numeric> = N extends N ? Numeric extends N ? boolean : `${N}` extends `-${Numeric}` ? false : true : never;
/**
* Check if a given numeric value is an integer
* @returns
* true if it is, else false
*/
type IsInteger<N extends Numeric> = number extends N ? false | true : N extends N ? `${N}` extends `${string}.${string}` ? false : true : never;
/**
* Check if a given numeric value is an float
* @returns
* true if it is, else false
*/
type IsFloat<N extends Numeric> = number extends N ? false | true : N extends N ? `${N}` extends `${string}.${string}` ? true : false : never;
/**
* @returns `true` if `T` is a `Newable`, otherwise `false`.
*/
type IsNewable<T> = IfExtends<T, Newable, true, false>;
/**
* Conditional type: if the condition `C` is `true`, return `Do`, otherwise return `Else`.
* @example
* ````ts
If<IsNever<never>, true, false>; // true
If<Not<IsNever<never>>, true, false>; // false
* ````
*/
type If<C extends boolean, Do, Else> = IfExtends<C, true, Do, Else>;
/**
* Negates a boolean type `B`.
* @example
Not<true>; // false
Not<false>; // true
*/
type Not<B extends boolean> = B extends true ? false : true;
/**
* Logical AND between two boolean types `B1` and `B2`.
* @example
And<true, false>; // false
And<true, true>; // true
*/
type And<B1 extends boolean, B2 extends boolean> = If<B1, If<B2, true, false>, false>;
/**
* Logical OR between two boolean types `B1` and `B2`.
* @example
Or<true, false>; // true
Or<false, false>; // false
*/
type Or<B1 extends boolean, B2 extends boolean> = If<B1, true, If<B2, true, false>>;
/**
* Exclusive OR (XOR) between two boolean types `B1` and `B2`.
* @example
Xor<true, false>; // true
Xor<true, true>; // false
*/
type Xor<B1 extends boolean, B2 extends boolean> = Or<And<B1, Not<B2>>, And<Not<B1>, B2>>;
/**
* Logical NAND between two boolean types `B1` and `B2`.
* @example
Nand<true, false>; // true
Nand<true, true>; // false
*/
type Nand<B1 extends boolean, B2 extends boolean> = Not<And<B1, B2>>;
/**
* Represents the keys of a given type `T`.
* This type alias `Keys<T>` is equivalent to `keyof T`,
* which retrieves the union type of keys (property names) of type `T`.
* @returns Union type of keys (property names) of type `T`.
* @example
* type Person = {
* name: string;
* age: number;
* email: string;
* };
*
* type PersonKeys = Keys<Person>; => "name" | "age" | "email"
*/
type Keys<T> = keyof T;
/**
* Represents the union type of values of properties in a given type `T`.
* This type alias `Vals<T>` retrieves the union type of values corresponding
* to the keys (property names) of type `T`.
* @example
* type Person = {
* name: string;
* age: number;
* email: string;
* };
*
* type PersonValues = Vals<Person>; => string | number
*/
type Vals<T> = T[Keys<T>];
/**
* Represents a type that can be either a single value of type `T` or an array of values of type `T`.
* @example
* type T1 = EitherOneOrMany<number>; 10; // Valid
* type T2 = EitherOneOrMany<number>; [20, 30]; // Also valid
*/
type EitherOneOrMany<T> = T | T[];
/**
* Turns a given primitive value (except symbol) into its string representation
* @example
* ```ts
StringifyPrimitive<45> // "45"
StringifyPrimitive<boolean> // "false" | "true"
StringifyPrimitive<null> // "null"
StringifyPrimitive<undefined> // "undefined"
* ```
*/
type StringifyPrimitive<P extends Exclude<Primitive, symbol>> = `${P}`;
/**
* Turn a given string literal to a numeric
* @example
* ````ts`
NumerifyString<'54'>; // 54
NumerifyString<'699620.000000001'>; // 699620.000000001
IsNegativeFloat<NumerifyString<'-699620.000000001'>>; // true
* ````
*/
type NumerifyString<S extends string> = S extends `${infer N extends Numeric}` ? N : never;
/**
* Check if a string starts with another string
* @example
* ```ts
* type Result = StringStartsWith<'hello world', 'hello'>; // Result: true
* ```
*/
type StringStartsWith<T extends string, U extends string> = IfExtends<T, `${U}${string}`, true, false>;
/**
* Check if a string starts with a given prefix and ends with a given suffix
* @example
* ```ts
* type Result = EnforcedString<'pk_123', 'pk_'>; // Result: 'pk_123'
*/
type EnforcedString<T extends string, Prefix extends string = '', Suffix extends string = ''> = StringStartsWith<T, Prefix> extends true ? StringEndsWith<T, Suffix> extends true ? T : never : never;
/**
* Check if a string ends with another string
* @example
* ```ts
* type Result = StringEndsWith<'hello world', 'world'>; // Result: true
* ```
*/
type StringEndsWith<T extends string, U extends string> = IfExtends<T, `${string}${U}`, true, false>;
/**
* Get the absolute value of a numeric N
* @example
* ```ts
* Abs<-54>; // Result: 54
* Abs<54>; // Result: 54
* ```
* @returns
* |N|
*/
type Abs<N extends Numeric> = `${N}` extends `-${infer M extends Numeric}` ? M : N;
/**
* Get the length of a given string
* @example
* ```ts
* Strlen<'foo'>; // Result: 3
* ```
*
* Can be used to create type constraints on string lengths:
* ```ts
* // Only allow strings of length 8-10 characters
* type ValidPassword<T extends string> = Strlen<T> extends 8 | 9 | 10 ? T : never;
*
* // Function that only accepts valid password strings
* function checkValidPassword<T extends string>(val: T & ValidPassword<T>) {
* console.log(val);
* }
*
* // Works with string literals
* const validPass = checkValidPassword('password123' as const); // OK
* const invalidPass = checkValidPassword('short' as const); // Type error
* ```
*/
type Strlen<S extends string, Arr extends any[] = EmptyArray> = S extends `${infer L}${infer R}` ? Strlen<R, [...Arr, L]> : Arr['length'];
/**
* Check if two strings S1 and S2 have the same length
* @returns
* ``true`` if they do, else ``false``
*/
type EqualStrlen<S1 extends string, S2 extends string> = Equals<Strlen<S1>, Strlen<S2>>;
type EmptyArray = [];
/**
* Conditional type that checks if type `T` extends type `P`.
* If `T` extends `P`, the type resolves to `Do`; otherwise `Else`.
* @example
* type Result1 = IfExtends<string, string | number, true, false>; // is true
* type Result2 = IfExtends<number, string | number, true, false>; // is true
* type Result3 = IfExtends<boolean, string | number, true, false>; // is false
*
* type IsString<T> = IfExtends<T, string, true, false>;
* type IsNumber<T> = IfExtends<T, number, true, false>;
*
* type TestString = IsString<string>; // is true
* type TestNumber = IsNumber<number>; // is true
* type TestBoolean = IsNumber<boolean>; // is false
*/
type IfExtends<T, P, Do, Else> = T extends P ? Do : Else;
/**
* Conditional type that checks if type `T` is equal to type `P`.
* If `T` is equal to `P`, the type resolves to `Do`, otherwise `Else`.
* @example
* type Result1 = IfEquals<string, string, true, false>; // is true
* type Result2 = IfEquals<number, string, true, false>; // is false
* type Result3 = IfEquals<boolean, boolean, true, false>; // is true
*
* type IsExactlyString<T> = IfEquals<T, string, true, false>;
* type IsExactlyNumber<T> = IfEquals<T, number, true, false>;
*
* type TestString = IsExactlyString<string>; // is true
* type TestNumber = IsExactlyNumber<number>; // is false
* type TestBoolean = IsExactlyString<boolean>; // is false
*/
type IfEquals<T, P, Do, Else> = Equals<T, P> extends true ? Do : Else;
/**
* Conditional type that checks if two types `X` and `Y` are exactly equal.
* If `X` is equal to `Y`, the type resolves to `true`; otherwise `false`.
* @example
* type Result1 = Equals<string, string>; // is true
* type Result2 = Equals<number, string>; // is false
* type Result3 = Equals<boolean | string, string | boolean>; // is true
*/
type Equals<X, Y> = (<T>() => T extends X ? true : false) extends <T>() => T extends Y ? true : false ? true : false;
/**
A type that excludes `null` and `undefined` from type `T`.
* @example
Type Str = ExcludeNullable<string | null> // string
Type Str2 = ExcludeNullable<string | null | undefined> // string
Type Str3 = ExcludeNullable<string | undefined> // string
*
*/
type ExcludeNullable<T> = Exclude<T, Nullable>;
/**
A type that excludes `undefined` from type `T`.
* @example
Type Str = ExcludeNullable<string | undefined> // Result: string
*/
type ExcludeUndefined<T> = Exclude<T, undefined>;
/**
* A type that excludes `null` from type `T`.
* @example
Type Str = ExcludeNullable<string | null> // Result: string
*/
type ExcludeNull<T> = Exclude<T, null>;
/**
* Get the literal names of keys that are methods in object type `T`
* @example
* ````ts
Methods<{
foo: () => void;
bar: (a: any) => string;
barBaz: string;
bazBar: Numeric;
}> // Result: 'foo' | 'bar'
* ````
*/
type Methods<T extends object> = {
[K in Keys<T>]-?: ExcludeNullable<T[K]> extends AnyFunction ? K : never;
}[Keys<T>];
/**
* Get the literal names of keys that are propeties, basically anything that's not a method in object type `T`
* @example
* ````ts
Properties<{
barBaz: string;
bazBar: Numeric;
bar: () => number;
}> // Result: 'barBaz' | 'bazBar'
* ````
*/
type Properties<T extends object> = {
[K in Keys<T>]-?: ExcludeNullable<T[K]> extends AnyFunction ? never : K;
}[Keys<T>];
/**
* Evaluates whether one type `T` is assignable to another type `U`.
* @returns `true` if `T` is assignable to `U`, `false` otherwise.
*
* @example
* ```typescript
* type A = { x: number };
* type B = { x: number; y: string };
* type C = { x: number; y?: string };
*
* type Check1 = Extends<A, B>; // false, A does not extend B
* type Check2 = Extends<B, A>; // true, B extends A
* type Check3 = Extends<C, B>; // true, C extends B
* ```
*/
type Extends<T, U> = T extends never ? false : T extends U ? true : false;
/**
* @hidden
*/
type _FlippableRecord = Record<string, string | number | boolean>;
/**
* Constructs a new type that takes an object type `T` and returns a new object type where the keys of `T` become
* the values and the values become the keys.
*
* @example
* ```typescript
* type Object1 = { name: 'John'; age: 30; city: 'New York' };
* type Flipped1 = Flip<Object1>; // {'John': 'name', 30: 'age', 'New York': 'city'}
*
* type Object2 = { fruit: 'Apple'; color: 'Red'; price: 1.25 };
* type Flipped2 = Flip<Object2>; // {'Apple': 'fruit', 'Red': 'color', 1.25: 'price'}
*
* type Object3 = { optionA: true; optionB: false };
* type Flipped3 = Flip<Object3>; // {true: 'optionA', false: 'optionB'}
* ```
*/
type Flip<T extends _FlippableRecord> = {
[P in Keys<T> as StringifyPrimitive<T[P]>]: P;
};
/**
* Constructs a new type by replacing keys of type `T` in object type `U` with corresponding keys from object type `Y`.
* @template U The original object type.
* @template T The type of keys to replace.
* @template Y The object type containing replacement keys.
*
* @example
* ```typescript
* type Original = {
* bar: string;
* foo: number;
* fooBar: string;
* };
*
* type Replacement = ReplaceKeys<Original, 'bar', { bar: number }>;
*
* // Result: { bar: number; foo: number; fooBar: string; }
*
* type AnotherReplacement = ReplaceKeys<Original, 'foo', { foo: boolean }>;
*
* // Result: { bar: string; foo: boolean; fooBar: string; }
* ```
*/
type ReplaceKeys<U, T, Y> = {
[K in Keys<U>]: IfExtends<K, T, K extends Keys<Y> ? Y[K] : never, U[K]>;
};
/**
* A type that recursively mutates all the proprties within a given object type `T`.
*
* @example
* ````ts
type Actual = {
readonly a: () => 1;
readonly x: string;
readonly s: {
readonly q: Nullable;
readonly s: {
readonly i: {
readonly x: {
readonly o: Maybe<Primitive>;
readonly n: Falsy;
};
readonly e: 'foo';
};
};
};
};
type Expected = {
a: () => 1;
x: string;
s: {
q: Nullable;
s: {
i: {
x: {
o: Maybe<Primitive>;
n: Falsy;
};
e: 'foo';
};
};
};
};
type T = DeepMutable<Actual>; // T Results in: Expected
* ````
*/
type DeepMutable<T> = T extends UnknownFunction ? T : {
-readonly [K in Keys<T>]: T[K] extends unknown ? DeepMutable<T[K]> : T[K];
};
/**
* Checks if all the nested properties of a given object T are actually mutable.
*/
type IsDeepMutable<T> = T extends DeepMutable<T> ? true : false;
/**
* Checks if all the nested properties of a given object T are actually immutable.
*/
type IsDeepImmutable<T> = T extends DeepImmutable<T> ? true : false;
/**
* Check if all the properties of a given object (nested) are required
* @returns
* `true` if all the properties are, otherwise `false`
*/
type IsDeepRequired<T> = IfExtends<T, DeepRequired<T>, true, false>;
/**
* Check if all the properties of a given object (nested) are non required
* @returns
* `true` if all the properties are, otherwise `false`
*/
type IsDeepNotRequired<T> = IfExtends<T, DeepNotRequired<T>, true, false>;
/**
* A type that recursively turns the proprties within a given object type `T` immutable.
* @example
* ````ts
type Actual = {
a: () => 1;
x: string;
s: {
q: Nullable;
s: {
i: {
x: {
o: Maybe<Primitive>;
n: Falsy;
};
e: 'foo';
};
};
};
};
type Expected = {
readonly a: () => 1;
readonly x: string;
readonly s: {
readonly q: Nullable;
readonly s: {
readonly i: {
readonly x: {
readonly o: Maybe<Primitive>;
readonly n: Falsy;
};
readonly e: 'foo';
};
};
};
};
type T = DeepImmutable<Actual>; // T Results in: Expected
* ````
* */
type DeepImmutable<T> = T extends UnknownFunction ? T : {
readonly [K in Keys<T>]: T[K] extends unknown ? DeepImmutable<T[K]> : T[K];
};
declare const __s: unique symbol;
/**
* This type represents a new unique type derived from an existing base type.
* It defines a mechanism similar to Python's [`NewType`](https://docs.python.org/3/library/typing.html#newtype).
* In TypeScript world it's refered to as 'Type Branding'.
*
* @template N The unique identifier for the new type.
* @template T The base type of the new type.
*
* @example
* type FooID = NewType<'FooID', string>;
* type BarID = NewType<'BarID', string>;
*
* const fooId: FooID = 'foo123' as FooID;
* const barId: BarID = 'bar456' as BarID;
*
* // Here's a potential bug:
* const buggyFooBar = (foo: string, bar: string) => {};
* buggyFooBar('bar456', 'foo123'); // this works but it's an undetected bug.
*
* // Bug mitigation:
* const safeFooBar = (foo: FooID, bar: BarID) => {};
* safeFooBar('bar456', 'foo123'); // TypeScript error: Argument of type 'string' is not assignable to parameter of type 'FooID'.
*/
type NewType<N, T> = T & {
/**
* Property `__s` is not intended for direct access nor modification.
* @internal
*/ [__s]: N;
};
/**
* Type that recursively omits specified nested properties from an object type.
* @template T The input object type.
* @template P A **string** literal representing the path of properties to omit (e.g., 'person.name.value').
* @example
* ```typescript
* type T =
* a: {
* b: string;
* b2: {
* c: {
* d: number;
* };
* };
* };
* }
*
* DeepOmit<T, 'a.b2.c'> // Results in: { a: { b: string; b2: {} } }
* ```
*/
type DeepOmit<T, P extends string> = P extends `${infer K}.${infer R}` ? {
[KT in Keys<T>]: KT extends K ? DeepOmit<T[KT], R> : T[KT];
} : Omit<T, P>;
/**
* As the name implies, it turns a union into an intersection
* @example
type T = NewType<'T', string>;
type Result = UnionToIntersection<
(() => 'foo') | ((baz: 88) => Optional<NewType<'T', string>>)
>
// Result: (() => 'foo') & ((baz: 88) => Optional<T>)
type Result2 = UnionToIntersection<
IsFalsy<0> | IsDeepImmutable<{ a: string; readonly b: string }>
>
// Result 2: IsFalsy<0> & IsDeepImmutable<{ a: string; readonly b: string }> => true & true => evaluates to true
*
*/
type UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends (arg: infer I) => void ? I : never;
/**
* @hidden
*/
type _FindPrimitive<T> = T extends string ? string : T extends symbol ? symbol : T extends boolean ? boolean : T extends null ? null : T extends number ? number : T extends bigint ? bigint : T extends undefined ? undefined : never;
/**
* @example
* Recursively transforms an object type `T` into a type where all properties
* are replaced with their corresponding primitive types.
* ````ts
type Actual = {
a: 'a';
b: 85;
c: true;
d: {
e: 'xxxxxxxxxxx';
f: 'eeeeeeeeeeeeeeeeee';
g: {
h: 1000000000000000;
i: undefined;
j: null;
};
};
};
type Expected = {
a: string;
b: number;
c: boolean;
d: {
e: string;
f: string;
g: {
h: number;
i: undefined;
j: null;
};
};
};
type Result = DeepToPrimitive<Actual>; // Expected
* ````
*/
type DeepToPrimitive<T> = {
[K in Keys<T>]: T[K] extends object ? DeepToPrimitive<T[K]> : _FindPrimitive<T[K]>;
};
/**
* @hidden
*/
type __Merge<T, S> = {
[K in Keys<T> | Keys<S>]: K extends Keys<S> ? S[K] : T[K & Keys<T>];
};
/**
* Copies all enumerable own properties from one target object
* to a source array of objects.
* @example
* ````ts
type T = Assign<{ a: 'd'; d: 'd' }, [{ a: 'a' }, { b: 'b' }, { c: 'c' }]>
// Result:
{
a: 'a';
b: 'b';
c: 'c';
d: 'd';
}
* ````
*/
type Assign<T extends Record<string, unknown>, Arr extends unknown[]> = Arr extends [infer S, ...infer E] ? Assign<S extends object ? __Merge<T, S> : T, E> : T;
/**
* Get the common keys between two objects, if a key is found to be shared between both,
* then the type of that key will take the first object's key type
* @returns
* An object that consist of what's common in both, else never
* @example
* ````ts
CommonKeys<
{ d: 'first objects d'; x: 'also first objects x' },
{ d: 'd'; p: { b: 'b' }; x: { c: 'c' } }
>
// Results in { d: 'first objects d'; x: 'also first objects x' }
* ````
*/
type PickCommonKeys<T extends Record<string, unknown>, U exte