inferred-types 0.35.0 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +2 -34
  2. package/dist/index.d.ts +1812 -1656
  3. package/dist/index.mjs +95 -26
  4. package/package.json +12 -11
  5. package/src/runtime/combinators/filter.ts +0 -1
  6. package/src/runtime/lists/asArray.ts +8 -4
  7. package/src/runtime/lists/createConverter.ts +62 -0
  8. package/src/runtime/lists/index.ts +1 -0
  9. package/src/runtime/literals/box.ts +41 -20
  10. package/src/runtime/literals/ensureLeading.ts +17 -0
  11. package/src/runtime/literals/ensureTrailing.ts +17 -0
  12. package/src/runtime/literals/index.ts +4 -2
  13. package/src/runtime/literals/pathJoin.ts +22 -7
  14. package/src/runtime/literals/{stripStarting.ts → stripLeading.ts} +4 -4
  15. package/src/runtime/literals/stripTrailing.ts +15 -0
  16. package/src/runtime/literals/wide.ts +13 -0
  17. package/src/runtime/type-checks/ifSameType.ts +20 -0
  18. package/src/runtime/type-checks/index.ts +1 -0
  19. package/src/runtime/type-checks/isBoolean.ts +2 -1
  20. package/src/runtime/type-checks/isFunction.ts +2 -4
  21. package/src/runtime/type-checks/isObject.ts +10 -2
  22. package/src/runtime/type-checks/isString.ts +1 -1
  23. package/src/runtime/type-checks/isUndefined.ts +8 -0
  24. package/src/types/alphabetic/EnsureLeading.ts +24 -0
  25. package/src/types/alphabetic/EnsureTrailing.ts +24 -0
  26. package/src/types/alphabetic/PathJoin.ts +43 -31
  27. package/src/types/alphabetic/{StripStarting.ts → StripLeading.ts} +1 -1
  28. package/src/types/alphabetic/{StripEnding.ts → StripTrailing.ts} +1 -1
  29. package/src/types/alphabetic/index.ts +5 -2
  30. package/src/types/boolean-logic/HasParameters.ts +21 -0
  31. package/src/types/boolean-logic/array.ts +2 -2
  32. package/src/types/boolean-logic/boolean.ts +1 -1
  33. package/src/types/boolean-logic/equivalency.ts +2 -2
  34. package/src/types/boolean-logic/index.ts +1 -0
  35. package/src/types/dictionary/props.ts +5 -0
  36. package/src/types/lists/ConvertAndMap.ts +151 -0
  37. package/src/types/type-conversion/Widen.ts +15 -0
  38. package/tests/boolean-logic/HasParameters.ts +29 -0
  39. package/tests/lists/asArray.test.ts +19 -1
  40. package/tests/literals/EnsureStripLeadingTrailing.test.ts +79 -0
  41. package/tests/literals/PathJoin.test.ts +44 -37
  42. package/tests/literals/box.test.ts +31 -23
  43. package/tests/runtime/if-is.spec.ts +66 -5
  44. package/tests/runtime/map-and-convert.test.ts +31 -0
  45. package/dist/index.js +0 -868
  46. package/src/runtime/literals/stripEnding.ts +0 -9
package/dist/index.mjs CHANGED
@@ -75,6 +75,11 @@ var condition = (c, input) => {
75
75
  return c(input);
76
76
  };
77
77
 
78
+ // src/runtime/type-checks/ifSameType.ts
79
+ function ifSameType(value, comparisonType, ifExtends, doesNotExtend) {
80
+ return typeof value === typeof comparisonType ? ifExtends(value) : doesNotExtend(value);
81
+ }
82
+
78
83
  // src/runtime/type-checks/isArray.ts
79
84
  function isArray(i) {
80
85
  return Array.isArray(i) === true;
@@ -129,6 +134,9 @@ function ifNumber(val, ifVal, elseVal) {
129
134
  function isObject(i) {
130
135
  return typeof i === "object" && i !== null && Array.isArray(i) === false;
131
136
  }
137
+ function ifObject(val, ifObj, notObj) {
138
+ return isObject(val) ? ifObj : notObj;
139
+ }
132
140
 
133
141
  // src/runtime/type-checks/isString.ts
134
142
  function isString(i) {
@@ -158,6 +166,9 @@ function isUndefined(i) {
158
166
  function ifUndefined(val, ifVal, elseVal) {
159
167
  return isUndefined(val) ? ifVal : elseVal;
160
168
  }
169
+ function ifDefined(val, ifVal, elseVal) {
170
+ return isUndefined(val) ? ifVal : elseVal;
171
+ }
161
172
 
162
173
  // src/runtime/runtime/type.ts
163
174
  var typeApi = () => ({
@@ -621,6 +632,69 @@ function groupBy(_data) {
621
632
  throw new Error("not implemented");
622
633
  }
623
634
 
635
+ // src/runtime/literals/box.ts
636
+ function box(value) {
637
+ const rtn = {
638
+ __kind: "box",
639
+ value,
640
+ unbox: (...p) => {
641
+ return typeof value === "function" ? value(...p) : value;
642
+ }
643
+ };
644
+ return rtn;
645
+ }
646
+ function isBox(thing) {
647
+ return typeof thing === "object" && "__kind" in thing && thing.__kind === "box";
648
+ }
649
+ function boxDictionaryValues(dict) {
650
+ return keys(dict).reduce(
651
+ (acc, key) => ({ ...acc, [key]: box(dict[key]) }),
652
+ {}
653
+ );
654
+ }
655
+ function unbox(val) {
656
+ return isBox(val) ? val.value : val;
657
+ }
658
+
659
+ // src/runtime/literals/wide.ts
660
+ var wide = {
661
+ boolean: false,
662
+ string: "",
663
+ number: 0,
664
+ symbol: Symbol(),
665
+ null: null,
666
+ undefined: void 0
667
+ };
668
+
669
+ // src/runtime/lists/createConverter.ts
670
+ function createConverter(mapper2) {
671
+ const converter = boxDictionaryValues(mapper2);
672
+ return (input) => {
673
+ const v = ifSameType(
674
+ input,
675
+ wide.string,
676
+ (i) => converter.string.unbox(i),
677
+ (i) => ifSameType(
678
+ i,
679
+ wide.number,
680
+ (i2) => converter.number.unbox(i2),
681
+ (i2) => ifSameType(
682
+ i2,
683
+ wide.boolean,
684
+ (i3) => converter.boolean.unbox(i3),
685
+ (i3) => ifSameType(
686
+ i3,
687
+ {},
688
+ (i4) => converter.object.unbox(i4),
689
+ (i4) => i4
690
+ )
691
+ )
692
+ )
693
+ );
694
+ return v;
695
+ };
696
+ }
697
+
624
698
  // src/runtime/literals/ExplicitFunction.ts
625
699
  function ExplicitFunction(fn) {
626
700
  return fn;
@@ -638,26 +712,10 @@ function arrayToObject(prop, unique) {
638
712
  return transform;
639
713
  }
640
714
 
641
- // src/runtime/literals/box.ts
642
- function box(value) {
643
- const rtn = {
644
- __kind: "box",
645
- value,
646
- unbox: () => value
647
- };
648
- return rtn;
649
- }
650
- function isBox(thing) {
651
- return typeof thing === "object" && "__kind" in thing && thing.__kind === "box";
652
- }
653
- function unbox(thing) {
654
- return isBox(thing) ? thing.unbox() : thing;
655
- }
656
-
657
715
  // src/runtime/literals/defineType.ts
658
716
  function defineType(literal2 = {}) {
659
- return (wide = {}) => {
660
- return literal2 ? { ...wide, ...literal2 } : wide;
717
+ return (wide2 = {}) => {
718
+ return literal2 ? { ...wide2, ...literal2 } : wide2;
661
719
  };
662
720
  }
663
721
 
@@ -681,16 +739,20 @@ function literal(obj) {
681
739
  return obj;
682
740
  }
683
741
 
684
- // src/runtime/literals/stripEnding.ts
685
- function stripEnding(content, strip) {
742
+ // src/runtime/literals/stripTrailing.ts
743
+ function stripTrailing(content, strip) {
686
744
  const re = new RegExp(`(.*)${strip}$`);
687
745
  return content.endsWith(strip) ? content.replace(re, "$1") : content;
688
746
  }
689
747
 
690
- // src/runtime/literals/stripStarting.ts
691
- function stripStarting(content, strip) {
692
- const re = new RegExp(`^${strip}(.*)`);
693
- return content.startsWith(strip) ? content.replace(re, "$1") : content;
748
+ // src/runtime/literals/ensureTrailing.ts
749
+ function ensureTrailing(content, ensure) {
750
+ return content.endsWith(ensure) ? content : `${content}${ensure}`;
751
+ }
752
+
753
+ // src/runtime/literals/ensureLeading.ts
754
+ function ensureLeading(content, ensure) {
755
+ return content.startsWith(ensure) ? content : `${ensure}${content}`;
694
756
  }
695
757
  export {
696
758
  Configurator,
@@ -706,13 +768,17 @@ export {
706
768
  arrayToObject,
707
769
  asArray,
708
770
  box,
771
+ boxDictionaryValues,
709
772
  condition,
773
+ createConverter,
710
774
  createFnWithProps,
711
775
  defineProperties,
712
776
  defineType,
713
777
  dictArr,
714
778
  dictToKv,
715
779
  dictionaryTransform,
780
+ ensureLeading,
781
+ ensureTrailing,
716
782
  entries,
717
783
  filter,
718
784
  filterDictArray,
@@ -724,9 +790,12 @@ export {
724
790
  ifArray,
725
791
  ifArrayPartial,
726
792
  ifBoolean,
793
+ ifDefined,
727
794
  ifFalse,
728
795
  ifNull,
729
796
  ifNumber,
797
+ ifObject,
798
+ ifSameType,
730
799
  ifString,
731
800
  ifTrue,
732
801
  ifUndefined,
@@ -760,10 +829,10 @@ export {
760
829
  readonlyFnWithProps,
761
830
  ruleSet,
762
831
  strArrayToDict,
763
- stripEnding,
764
- stripStarting,
832
+ stripTrailing,
765
833
  type,
766
834
  typeApi,
767
835
  unbox,
836
+ wide,
768
837
  withValue
769
838
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inferred-types",
3
- "version": "0.35.0",
3
+ "version": "0.36.1",
4
4
  "description": "Functions which provide useful type inference on TS projects",
5
5
  "license": "MIT",
6
6
  "author": "Ken Snyder<ken@ken.net>",
@@ -16,14 +16,15 @@
16
16
  "autoindex": "npx dd autoindex",
17
17
  "build": "run-s clean autoindex lint build:bundle",
18
18
  "build:force": "run-s clean autoindex build:bundle",
19
- "build:bundle": "npx tsup src/index.ts --dts --format=esm,cjs --clean",
19
+ "build:bundle": "npx tsup src/index.ts --dts --format=esm --clean",
20
20
  "watch": "run-p watch:*",
21
21
  "watch:autoindex": "npx dd autoindex --watch",
22
- "watch:bundle": "npx tsup src/index.ts --dts --format=esm,cjs --watch",
22
+ "watch:bundle": "npx tsup src/index.ts --dts --format=esm --watch",
23
23
  "clean": "rimraf dist/**/*",
24
24
  "lint": "eslint src/**/*.ts --fix && tsc --noEmit ",
25
25
  "lint:full": "eslint src/**/*.ts && eslint tests/**/*.ts && tsc --noEmit",
26
26
  "test": "vitest",
27
+ "test:ui": "vitest --ui",
27
28
  "test:ci": "vitest run",
28
29
  "audit:fix": "pnpm audit --fix",
29
30
  "release": "run-s lint:full release:latest test:ci audit:fix release:bump",
@@ -32,10 +33,10 @@
32
33
  },
33
34
  "devDependencies": {
34
35
  "@type-challenges/utils": "~0.1.1",
35
- "@types/node": "^16.18.4",
36
- "@typescript-eslint/eslint-plugin": "^5.45.1",
37
- "@typescript-eslint/parser": "^5.45.1",
38
- "@vitest/ui": "^0.25.4",
36
+ "@types/node": "^16.18.7",
37
+ "@typescript-eslint/eslint-plugin": "^5.46.0",
38
+ "@typescript-eslint/parser": "^5.46.0",
39
+ "@vitest/ui": "^0.25.6",
39
40
  "bumpp": "^8.2.1",
40
41
  "common-types": "^1.33.2",
41
42
  "cross-env": "^7.0.3",
@@ -48,16 +49,16 @@
48
49
  "eslint-plugin-promise": "^6.1.1",
49
50
  "npm-run-all": "~4.1.5",
50
51
  "pathe": "^1.0.0",
51
- "prettier": "~2.8.0",
52
+ "prettier": "~2.8.1",
52
53
  "rimraf": "^3.0.2",
53
54
  "sharp": "^0.31.2",
54
55
  "tsup": "^6.5.0",
55
- "typescript": "^4.9.3",
56
+ "typescript": "^4.9.4",
56
57
  "vite": "^3.2.5",
57
- "vitest": "^0.25.4"
58
+ "vitest": "^0.25.6"
58
59
  },
59
60
  "dependencies": {
60
- "brilliant-errors": "^0.6.1"
61
+ "brilliant-errors": "^0.7.1"
61
62
  },
62
63
  "pnpm": {
63
64
  "overrides": {
@@ -109,7 +109,6 @@ export type LogicalCombinator = "AND" | "OR";
109
109
  * const onlyPrivate = filter({ startsWith: "_" });
110
110
  * const privateFiles = files.filter(onlyPrivate);
111
111
  * ```
112
- *
113
112
  */
114
113
  export type FilterFn<T extends StringFilter | NumericFilter> = T extends StringFilter
115
114
  ? <V extends string | undefined>(input: V) => boolean
@@ -27,8 +27,12 @@ export type AsArray<T, W extends boolean = false> = T extends any[]
27
27
  */
28
28
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
29
29
  export const asArray = <T extends Narrowable, W extends boolean = true>(thing: T, _widen?: W) => {
30
- return (isArray(thing) ? thing : typeof thing === "undefined" ? ([] as T[]) : [thing]) as AsArray<
31
- T,
32
- W
33
- >;
30
+ return (
31
+ isArray(thing)
32
+ ? // proxy thing back as it's already an array
33
+ thing
34
+ : typeof thing === "undefined"
35
+ ? ([] as T[])
36
+ : [thing]
37
+ ) as AsArray<T, W>;
34
38
  };
@@ -0,0 +1,62 @@
1
+ import { Narrowable } from "src/types";
2
+ import { ConverterShape, AvailableConverters } from "src/types/lists/ConvertAndMap";
3
+ import { boxDictionaryValues } from "../literals/box";
4
+ import { wide } from "../literals/wide";
5
+ import { ifSameType } from "../type-checks";
6
+
7
+ /**
8
+ * **createConverter**(mapper)
9
+ *
10
+ * A runtime utility which allows for the creation of a function which
11
+ * receives multiple wide types (string, number, boolean, object) and then transform it
12
+ * based on the "wide type" but while retaining the potentially narrow values passed in.
13
+ *
14
+ * The number of wide types which the converter will accept is based on how it configured
15
+ * as there are discrete functions which must be passed in for handling: strings, numbers,
16
+ * booleans, and "objects" (aka, Record<string,any>).
17
+ *
18
+ * ```ts
19
+ * // handles strings and numbers
20
+ * const convert = createConverter({
21
+ * string: s => `the string was ${s}`,
22
+ * number: n => `the number was ${n}`,
23
+ * });
24
+ * ```
25
+ */
26
+ export function createConverter<
27
+ S extends Narrowable = undefined,
28
+ N extends Narrowable = undefined,
29
+ B extends Narrowable = undefined,
30
+ O extends Narrowable = undefined
31
+ >(mapper: Partial<ConverterShape<S, N, B, O>>) {
32
+ type Mapper = Required<typeof mapper>;
33
+ const converter = boxDictionaryValues(mapper as Mapper);
34
+
35
+ return <T extends AvailableConverters<S, N, B, O>>(input: T) => {
36
+ const v = ifSameType(
37
+ input,
38
+ wide.string,
39
+ <T extends string>(i: T) => converter.string.unbox(i),
40
+ (i) =>
41
+ ifSameType(
42
+ i,
43
+ wide.number,
44
+ (i) => converter.number.unbox(i),
45
+ (i) =>
46
+ ifSameType(
47
+ i,
48
+ wide.boolean,
49
+ (i) => converter.boolean.unbox(i),
50
+ (i) =>
51
+ ifSameType(
52
+ i,
53
+ {} as Record<string, any>,
54
+ (i) => converter.object.unbox(i),
55
+ (i) => i as unknown
56
+ )
57
+ )
58
+ )
59
+ );
60
+ return v;
61
+ };
62
+ }
@@ -7,6 +7,7 @@
7
7
  // file exports
8
8
  export * from "./asArray";
9
9
  export * from "./groupBy";
10
+ export * from "./createConverter";
10
11
 
11
12
  // #endregion auto-indexed files
12
13
 
@@ -1,5 +1,7 @@
1
- import { AfterFirst, Narrowable } from "src/types";
1
+ import { AfterFirst, HasParameters, Narrowable } from "src/types";
2
2
  import { First } from "src/types/lists/First";
3
+ import { keys } from "../keys";
4
+ import { AnyFunction } from "../type-checks";
3
5
 
4
6
  export interface Box<T> {
5
7
  __kind: "box";
@@ -7,22 +9,16 @@ export interface Box<T> {
7
9
  /**
8
10
  * Unbox the boxed value in the narrowest possible type.
9
11
  *
10
- * **Note:** _if you want a wider type definition use `wide()`
11
- * instead._
12
+ * **note:** if the boxed value is a function with parameters you
13
+ * can pass the parameters directly into the `b.unbox(params)` call.
12
14
  */
13
- unbox(): T;
14
- // /**
15
- // * If the boxed value is a function with generics then you have opportunity to
16
- // * _narrow_ the type definition over time. This is achieved in a type strong manner,
17
- // * so you can't change the fundamental type but this example will work as expected:
18
- // * ```ts
19
- // * const fn = <T extends string>(name: T) => `Hello ${name}` as const;
20
- // * const b = box(fn);
21
- // * // later
22
- // * const b2 = b.narrow<"foo" | "bar">();
23
- // * ```
24
- // */
25
- // narrow: NarrowBox<T>;
15
+ unbox: HasParameters<Box<T>["value"]> extends true
16
+ ? Box<T>["value"] extends AnyFunction
17
+ ? Box<T>["value"] extends (...args: infer A) => infer R
18
+ ? <N extends A>(...args: N) => R
19
+ : () => ReturnType<T>
20
+ : () => T
21
+ : () => T;
26
22
  }
27
23
 
28
24
  export type BoxValue<T extends Box<any>> = T extends Box<infer V> ? V : never;
@@ -58,20 +54,45 @@ export function box<T extends Narrowable>(value: T): Box<T> {
58
54
  const rtn: Box<T> = {
59
55
  __kind: "box",
60
56
  value,
61
- unbox: () => value,
57
+ unbox: (<P extends any[], R extends Narrowable>(...p: P): R => {
58
+ return typeof value === "function" ? value(...p) : value;
59
+ }) as Box<T>["unbox"],
62
60
  };
63
61
 
64
62
  return rtn;
65
63
  }
66
64
 
67
- export function isBox(thing: unknown): thing is Box<any> {
65
+ export function isBox(thing: Narrowable): thing is Box<any> {
68
66
  return (
69
67
  typeof thing === "object" && "__kind" in (thing as object) && (thing as any).__kind === "box"
70
68
  );
71
69
  }
72
70
 
71
+ /**
72
+ * **boxDictionaryValues**(dict)
73
+ *
74
+ * Runtime utility which boxes each value in a dictionary
75
+ */
76
+ export function boxDictionaryValues<T extends {}>(dict: T) {
77
+ return keys(dict).reduce(
78
+ (acc, key) => ({ ...acc, [key]: box(dict[key]) }),
79
+ {} as {
80
+ [K in keyof T]: Box<T[K]>;
81
+ }
82
+ );
83
+ }
84
+
73
85
  export type Unbox<T> = T extends Box<infer U> ? U : T;
74
86
 
75
- export function unbox<T>(thing: T): Unbox<T> {
76
- return isBox(thing) ? thing.unbox() : thing;
87
+ //TODO: it would make sense in the future to use `b.unbox` instead
88
+ // of `b.value` to keep consistent but currently value behaves more
89
+ // consistently and with somewhat stronger typing
90
+
91
+ /**
92
+ * **unbox**(maybeBox)
93
+ *
94
+ * Unboxes a value if it was a box; otherwise it leaves _as is_.
95
+ */
96
+ export function unbox<T>(val: T): Unbox<T> {
97
+ return isBox(val) ? val.value : val;
77
98
  }
@@ -0,0 +1,17 @@
1
+ import { EnsureLeading } from "src/types/alphabetic";
2
+
3
+ /**
4
+ * **ensureLeading**(content, strip)
5
+ *
6
+ * Runtime utility which ensures that last part of a string -- `content` -- has the
7
+ * substring `ensure` at the end and adds it if not present.
8
+ */
9
+ export function ensureLeading<T extends string, U extends string>(
10
+ content: T,
11
+ ensure: U
12
+ ): EnsureLeading<T, U> {
13
+ return (
14
+ //
15
+ (content.startsWith(ensure) ? content : `${ensure}${content}`) as EnsureLeading<T, U>
16
+ );
17
+ }
@@ -0,0 +1,17 @@
1
+ import { EnsureTrailing } from "../../types/alphabetic/EnsureTrailing";
2
+
3
+ /**
4
+ * **ensureTrailing**(content, strip)
5
+ *
6
+ * Runtime utility which ensures that last part of a string -- `content` -- has the
7
+ * substring `ensure` at the end and adds it if not present.
8
+ */
9
+ export function ensureTrailing<T extends string, U extends string>(
10
+ content: T,
11
+ ensure: U
12
+ ): EnsureTrailing<T, U> {
13
+ return (
14
+ //
15
+ (content.endsWith(ensure) ? content : `${content}${ensure}`) as EnsureTrailing<T, U>
16
+ );
17
+ }
@@ -10,9 +10,11 @@ export * from "./box";
10
10
  export * from "./defineType";
11
11
  export * from "./identity";
12
12
  export * from "./literal";
13
- export * from "./stripEnding";
14
- export * from "./stripStarting";
13
+ export * from "./stripTrailing";
14
+ export * from "./ensureTrailing";
15
+ export * from "./ensureLeading";
15
16
  export * from "./Suggest";
17
+ export * from "./wide";
16
18
 
17
19
  // #endregion auto-indexed files
18
20
 
@@ -1,17 +1,32 @@
1
1
  import { PathJoin } from "src/types/alphabetic/PathJoin";
2
- import { stripStarting } from "./stripStarting";
3
- import { stripEnding } from "./stripEnding";
2
+ import { ensureLeading } from "./ensureLeading";
3
+ import { ensureTrailing } from "./ensureTrailing";
4
+ import { stripLeading } from "./stripLeading";
5
+ import { stripTrailing } from "./stripTrailing";
4
6
 
5
7
  /**
6
- * **pathJoin**`<T,U>()`
8
+ * **pathJoin**`<T,U>(begin, ...rest)`
7
9
  *
8
10
  * Run time utility which joins two strings together with
9
11
  * the intent to have them be divided by a Posix `/` character
10
12
  * appropriate for Unix file paths and URLs.
13
+ *
14
+ * **note:** to support more than two paths being joined there
15
+ * is now the ability to add a tuple of paths into the _rest_
16
+ * parameter
11
17
  */
12
- export function pathJoin<T extends string, U extends string>(begin: T, finish: U): PathJoin<T, U> {
13
- const start = stripEnding(begin, "/");
14
- const end = stripStarting(finish, "/");
18
+ export function pathJoin<T extends string, U extends readonly string[]>(
19
+ begin: T,
20
+ ...rest: U
21
+ ): PathJoin<T, U> {
22
+ const start = ensureTrailing(begin, "/");
23
+ const end = ensureLeading(rest.slice(-1)[0], "/");
24
+ const middle = rest
25
+ .slice(0, rest.length - 1)
26
+ .map((i) => stripLeading(stripTrailing(i, "/"), '"'));
27
+ const midString = stripTrailing(stripLeading(middle.join("/"), "/"), "/");
15
28
 
16
- return `${start}/${end}` as PathJoin<T, U>;
29
+ return (
30
+ rest.length > 1 ? `${start}${midString}${end}` : `${start}${stripLeading(end, "/")}`
31
+ ) as PathJoin<T, U>;
17
32
  }
@@ -1,9 +1,9 @@
1
- import { StripStarting } from "src/types/alphabetic/StripStarting";
1
+ import { StripLeading } from "src/types/alphabetic/StripLeading";
2
2
 
3
- export function stripStarting<T extends string, U extends string>(
3
+ export function stripLeading<T extends string, U extends string>(
4
4
  content: T,
5
5
  strip: U
6
- ): StripStarting<T, U> {
6
+ ): StripLeading<T, U> {
7
7
  const re = new RegExp(`^${strip}(.*)`);
8
8
  return (
9
9
  content.startsWith(strip)
@@ -11,5 +11,5 @@ export function stripStarting<T extends string, U extends string>(
11
11
  content.replace(re, "$1")
12
12
  : // does not
13
13
  content
14
- ) as StripStarting<T, U>;
14
+ ) as StripLeading<T, U>;
15
15
  }
@@ -0,0 +1,15 @@
1
+ import { StripTrailing } from "src/types/alphabetic/StripTrailing";
2
+
3
+ /**
4
+ * **stripTrailing**(content, strip)
5
+ *
6
+ * Runtime utility which ensures that last part of a string has substring
7
+ * removed if it exists and that strong typing is preserved.
8
+ */
9
+ export function stripTrailing<T extends string, U extends string>(
10
+ content: T,
11
+ strip: U
12
+ ): StripTrailing<T, U> {
13
+ const re = new RegExp(`(.*)${strip}$`);
14
+ return (content.endsWith(strip) ? content.replace(re, "$1") : content) as StripTrailing<T, U>;
15
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * **wide**
3
+ *
4
+ * Provides a dictionary of _wide_ types
5
+ */
6
+ export const wide = {
7
+ boolean: false as boolean,
8
+ string: "" as string,
9
+ number: 0 as number,
10
+ symbol: Symbol() as Symbol,
11
+ null: null,
12
+ undefined: undefined,
13
+ } as const;
@@ -0,0 +1,20 @@
1
+ import { Narrowable, Not, Widen } from "src/types";
2
+
3
+ export function ifSameType<
4
+ TValue extends Narrowable,
5
+ TType extends string | number | boolean | object,
6
+ IF extends Narrowable,
7
+ ELSE extends Narrowable
8
+ >(
9
+ value: TValue,
10
+ comparisonType: TType,
11
+ ifExtends: <T extends TType & TValue>(v: T) => IF,
12
+ doesNotExtend: (v: Not<TValue, TType>) => ELSE
13
+ ) {
14
+ return (
15
+ // runtime values match
16
+ (
17
+ typeof value === typeof comparisonType ? ifExtends(value as any) : doesNotExtend(value as any)
18
+ ) as Widen<TValue> extends Widen<TType> ? IF : ELSE
19
+ );
20
+ }
@@ -5,6 +5,7 @@
5
5
  // hash-code: 4324574d
6
6
 
7
7
  // file exports
8
+ export * from "./ifSameType";
8
9
  export * from "./isArray";
9
10
  export * from "./isBoolean";
10
11
  export * from "./isFalse";
@@ -1,9 +1,10 @@
1
+ import { Narrowable } from "src/types";
1
2
  import { IsBoolean } from "src/types/boolean-logic";
2
3
 
3
4
  /**
4
5
  * Runtime and type checks whether a variable is a boolean value.
5
6
  */
6
- export function isBoolean<T extends any>(i: T) {
7
+ export function isBoolean<T extends Narrowable>(i: T): IsBoolean<T> {
7
8
  return (typeof i === "boolean") as IsBoolean<T>;
8
9
  }
9
10
 
@@ -5,11 +5,9 @@ export type IsFunction<T> = T extends FunctionType ? true : false;
5
5
  export type HybridFunction<TProps extends {}> = (<TArgs extends any[]>(...args: TArgs) => any) &
6
6
  TProps;
7
7
 
8
- export type SimpleFunction = <TArgs extends any[]>(...args: TArgs) => any;
8
+ export type SimpleFunction = (...args: any[]) => any;
9
9
 
10
- export type AnyFunction<TProps extends {} | never = never> = TProps extends {}
11
- ? HybridFunction<TProps>
12
- : SimpleFunction;
10
+ export type AnyFunction<TProps extends {} = {}> = SimpleFunction | HybridFunction<TProps>;
13
11
 
14
12
  /**
15
13
  * Checks whether a passed in value is a function and ensures run-time and types
@@ -1,5 +1,5 @@
1
1
  import { FunctionType, Narrowable, Not } from "src/types";
2
- import { IsObject } from "src/types/boolean-logic";
2
+ import { IfObject, IsObject } from "src/types/boolean-logic";
3
3
 
4
4
  export type ObjectType = Not<Record<string, Narrowable>, FunctionType>;
5
5
 
@@ -9,6 +9,14 @@ export type ObjectType = Not<Record<string, Narrowable>, FunctionType>;
9
9
  * arrays are excluded, as well as functions which also have properties hanging
10
10
  * off of them.
11
11
  */
12
- export function isObject<T extends unknown>(i: T) {
12
+ export function isObject<T extends Narrowable>(i: T) {
13
13
  return (typeof i === "object" && i !== null && Array.isArray(i) === false) as IsObject<T>;
14
14
  }
15
+
16
+ export function ifObject<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable>(
17
+ val: T,
18
+ ifObj: IF,
19
+ notObj: ELSE
20
+ ): IfObject<T, IF, ELSE> {
21
+ return (isObject(val) ? ifObj : notObj) as IfObject<T, IF, ELSE>;
22
+ }