inferred-types 0.34.2 → 0.36.0

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 (45) hide show
  1. package/README.md +2 -34
  2. package/dist/index.d.ts +1796 -1624
  3. package/dist/index.mjs +101 -18
  4. package/package.json +14 -13
  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 -0
  13. package/src/runtime/literals/pathJoin.ts +32 -0
  14. package/src/runtime/literals/stripLeading.ts +15 -0
  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 +52 -0
  27. package/src/types/alphabetic/StripLeading.ts +23 -0
  28. package/src/types/alphabetic/StripTrailing.ts +23 -0
  29. package/src/types/alphabetic/index.ts +4 -0
  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 +94 -0
  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 -852
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
 
@@ -680,6 +738,22 @@ function idTypeGuard(_o) {
680
738
  function literal(obj) {
681
739
  return obj;
682
740
  }
741
+
742
+ // src/runtime/literals/stripTrailing.ts
743
+ function stripTrailing(content, strip) {
744
+ const re = new RegExp(`(.*)${strip}$`);
745
+ return content.endsWith(strip) ? content.replace(re, "$1") : content;
746
+ }
747
+
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}`;
756
+ }
683
757
  export {
684
758
  Configurator,
685
759
  DEFAULT_MANY_TO_ONE_MAPPING,
@@ -694,13 +768,17 @@ export {
694
768
  arrayToObject,
695
769
  asArray,
696
770
  box,
771
+ boxDictionaryValues,
697
772
  condition,
773
+ createConverter,
698
774
  createFnWithProps,
699
775
  defineProperties,
700
776
  defineType,
701
777
  dictArr,
702
778
  dictToKv,
703
779
  dictionaryTransform,
780
+ ensureLeading,
781
+ ensureTrailing,
704
782
  entries,
705
783
  filter,
706
784
  filterDictArray,
@@ -712,9 +790,12 @@ export {
712
790
  ifArray,
713
791
  ifArrayPartial,
714
792
  ifBoolean,
793
+ ifDefined,
715
794
  ifFalse,
716
795
  ifNull,
717
796
  ifNumber,
797
+ ifObject,
798
+ ifSameType,
718
799
  ifString,
719
800
  ifTrue,
720
801
  ifUndefined,
@@ -748,8 +829,10 @@ export {
748
829
  readonlyFnWithProps,
749
830
  ruleSet,
750
831
  strArrayToDict,
832
+ stripTrailing,
751
833
  type,
752
834
  typeApi,
753
835
  unbox,
836
+ wide,
754
837
  withValue
755
838
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inferred-types",
3
- "version": "0.34.2",
3
+ "version": "0.36.0",
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,32 +33,32 @@
32
33
  },
33
34
  "devDependencies": {
34
35
  "@type-challenges/utils": "~0.1.1",
35
- "@types/node": "^16.18.3",
36
- "@typescript-eslint/eslint-plugin": "^5.45.0",
37
- "@typescript-eslint/parser": "^5.45.0",
38
- "@vitest/ui": "^0.25.3",
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",
42
43
  "dd": "^0.25.4",
43
44
  "dotenv": "^16.0.3",
44
- "eslint": "^8.28.0",
45
+ "eslint": "^8.29.0",
45
46
  "eslint-config-prettier": "^8.5.0",
46
47
  "eslint-plugin-import": "^2.26.0",
47
48
  "eslint-plugin-prettier": "^4.2.1",
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
- "vite": "^3.2.4",
57
- "vitest": "^0.25.3"
56
+ "typescript": "^4.9.4",
57
+ "vite": "^3.2.5",
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,7 +10,11 @@ export * from "./box";
10
10
  export * from "./defineType";
11
11
  export * from "./identity";
12
12
  export * from "./literal";
13
+ export * from "./stripTrailing";
14
+ export * from "./ensureTrailing";
15
+ export * from "./ensureLeading";
13
16
  export * from "./Suggest";
17
+ export * from "./wide";
14
18
 
15
19
  // #endregion auto-indexed files
16
20
 
@@ -0,0 +1,32 @@
1
+ import { PathJoin } from "src/types/alphabetic/PathJoin";
2
+ import { ensureLeading } from "./ensureLeading";
3
+ import { ensureTrailing } from "./ensureTrailing";
4
+ import { stripLeading } from "./stripLeading";
5
+ import { stripTrailing } from "./stripTrailing";
6
+
7
+ /**
8
+ * **pathJoin**`<T,U>(begin, ...rest)`
9
+ *
10
+ * Run time utility which joins two strings together with
11
+ * the intent to have them be divided by a Posix `/` character
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
17
+ */
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("/"), "/"), "/");
28
+
29
+ return (
30
+ rest.length > 1 ? `${start}${midString}${end}` : `${start}${stripLeading(end, "/")}`
31
+ ) as PathJoin<T, U>;
32
+ }
@@ -0,0 +1,15 @@
1
+ import { StripLeading } from "src/types/alphabetic/StripLeading";
2
+
3
+ export function stripLeading<T extends string, U extends string>(
4
+ content: T,
5
+ strip: U
6
+ ): StripLeading<T, U> {
7
+ const re = new RegExp(`^${strip}(.*)`);
8
+ return (
9
+ content.startsWith(strip)
10
+ ? // starts with
11
+ content.replace(re, "$1")
12
+ : // does not
13
+ content
14
+ ) as StripLeading<T, U>;
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
+ }
@@ -27,7 +27,7 @@ export function isString<T>(i: T) {
27
27
  */
28
28
  export function ifString<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable>(
29
29
  val: T,
30
- ifVal: IF,
30
+ ifVal: <E extends string>(t: E & T) => IF,
31
31
  elseVal: ELSE
32
32
  ) {
33
33
  return (isString(val) ? ifVal : elseVal) as IfString<T, IF, ELSE>;