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
@@ -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>;
@@ -23,3 +23,11 @@ export function ifUndefined<T extends Narrowable, IF extends Narrowable, ELSE ex
23
23
  ) {
24
24
  return (isUndefined(val) ? ifVal : elseVal) as IsUndefined<T> extends true ? IF : ELSE;
25
25
  }
26
+
27
+ export function ifDefined<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable>(
28
+ val: T,
29
+ ifVal: <V extends Exclude<T, undefined>>(v: V) => IF,
30
+ elseVal: ELSE
31
+ ) {
32
+ return (isUndefined(val) ? ifVal : elseVal) as IsUndefined<T> extends true ? IF : ELSE;
33
+ }
@@ -0,0 +1,24 @@
1
+ import { IfLiteral } from "../boolean-logic/IsLiteral";
2
+
3
+ /**
4
+ * **EnsureLeading**`<T, U>`
5
+ *
6
+ * Will ensure that `T` ends with the _substring_ `U` when
7
+ * both are string literals.
8
+ *
9
+ * ```ts
10
+ * type T = " World";
11
+ * type U = "Hello";
12
+ * // "Hello World"
13
+ * type R = EnsureLeading<T,U>;
14
+ * ```
15
+ */
16
+ export type EnsureLeading<T extends string, U extends string> = IfLiteral<
17
+ // can only operate on literal strings
18
+ T,
19
+ // this path represents successful strip opp
20
+ // but we must never accept `U` being wide
21
+ string extends U ? never : T extends `${U}${string}` ? T : `${U}${T}`,
22
+ // here we must stay wide
23
+ string
24
+ >;
@@ -0,0 +1,24 @@
1
+ import { IfLiteral } from "../boolean-logic/IsLiteral";
2
+
3
+ /**
4
+ * **EnsureTrailing**`<T, U>`
5
+ *
6
+ * Will ensure that `T` ends with the substring `U` when
7
+ * both are string literals.
8
+ *
9
+ * ```ts
10
+ * type T = "Hello";
11
+ * type U = " World";
12
+ * // "Hello World"
13
+ * type R = EnsureTrailing<T,U>;
14
+ * ```
15
+ */
16
+ export type EnsureTrailing<T extends string, U extends string> = IfLiteral<
17
+ // can only operate on literal strings
18
+ T,
19
+ // this path represents successful strip opp
20
+ // but we must never accept `U` being wide
21
+ string extends U ? never : T extends `${string}${U}` ? T : `${T}${U}`,
22
+ // here we must stay wide
23
+ string
24
+ >;
@@ -1,40 +1,52 @@
1
1
  import { IfLiteral } from "../boolean-logic";
2
- import { StripEnding } from "./StripEnding";
3
- import { StripStarting } from "./StripStarting";
2
+ import { AfterFirst, First } from "../lists";
3
+ import { StripTrailing } from "./StripTrailing";
4
+ import { StripLeading } from "./StripLeading";
5
+
4
6
  /**
5
7
  * **PathJoin**`<T,U>`
6
8
  *
7
- * Type utility which joins two strings together with the
8
- * goal of making it a valid file or URI path (based on the
9
- * Posix standard for file paths).
9
+ * Type utility meant to bring 2 or more "path" strings together into
10
+ * a valid "path". Where a "path" is represented as nothing more than
11
+ * string characters delimited by a Posix `\` character.
10
12
  *
11
- * Primarily, this means that it ensures that both `T` and `U`
12
- * are separated by a single `/` character. Of course if either
13
- * `T` or `U` are _wide_ string types then the resulting type
14
- * will be more limited.
15
- *
16
- * **Note:** that in the case that both `T` and `U` are wide,
17
- * we opt to type the result as just a _string_ rather than
18
- * `${string}/${string}` which might be more precise 99% of
19
- * the time but where `T` is an empty string there actually
20
- * is no guarantee of a `/` character. Similarly we must
21
- * type the case where `U` is narrow but `T` is wide as
22
- * being `${string}${U}` instead of `${string}/${U}`.
13
+ * Note that the first part of the path will retain it's `\` if present
14
+ * and the last one will preserve it's `\` character if present. You can
15
+ * combine this utility with `EnsureTrailing<T>`, `StripTrailing<T>`,
16
+ * `EnsureStarting<T>`, and `StripStarting<T>` to further shape
17
+ * the type.
23
18
  */
24
19
  export type PathJoin<
25
20
  // leading string
26
21
  T extends string,
27
- // trailing string
28
- U extends string
29
- > = IfLiteral<
30
- T,
31
- // Literal T guaranteed
32
- IfLiteral<
33
- // conditional
34
- U,
35
- `${StripEnding<T, "/">}/${StripStarting<U, "/">}`,
36
- `${StripEnding<T, "/">}/${string}`
37
- >,
38
- // wide `T` encountered
39
- IfLiteral<U, `${string}${U}`, string>
40
- >;
22
+ // trailing string or strings
23
+ U extends string | readonly string[]
24
+ > = U extends readonly string[]
25
+ ? // eslint-disable-next-line no-use-before-define
26
+ PathMultiJoin<PathJoin<T, "">, [...U]>
27
+ : U extends string
28
+ ? IfLiteral<
29
+ T,
30
+ // Literal T guaranteed
31
+ IfLiteral<
32
+ // conditional
33
+ U,
34
+ `${StripTrailing<T, "/">}/${StripLeading<U, "/">}`,
35
+ `${StripTrailing<T, "/">}/${string}`
36
+ >,
37
+ // wide `T` encountered
38
+ IfLiteral<U, `${string}${U}`, string>
39
+ >
40
+ : never;
41
+
42
+ type PathMultiJoin<
43
+ TProcessed extends string,
44
+ TRemaining extends readonly string[]
45
+ > = [] extends TRemaining
46
+ ? TProcessed
47
+ : PathMultiJoin<
48
+ // add to the TProcessed string
49
+ PathJoin<TProcessed, First<TRemaining>>,
50
+ // remaining elements after extracting head element
51
+ AfterFirst<TRemaining>
52
+ >;
@@ -12,7 +12,7 @@ import { IfLiteral } from "../boolean-logic/IsLiteral";
12
12
  * type R = StripStarting<T,U>;
13
13
  * ```
14
14
  */
15
- export type StripStarting<T extends string, U extends string> = IfLiteral<
15
+ export type StripLeading<T extends string, U extends string> = IfLiteral<
16
16
  // can only operate on literal strings
17
17
  T,
18
18
  // this path represents successful strip opp
@@ -12,7 +12,7 @@ import { IfLiteral } from "../boolean-logic/IsLiteral";
12
12
  * type R = StripEnding<T,U>;
13
13
  * ```
14
14
  */
15
- export type StripEnding<T extends string, U extends string> = IfLiteral<
15
+ export type StripTrailing<T extends string, U extends string> = IfLiteral<
16
16
  // can only operate on literal strings
17
17
  T,
18
18
  // this path represents successful strip opp
@@ -12,14 +12,17 @@ export * from "./DashUppercase";
12
12
  export * from "./Cardinality";
13
13
  export * from "./Dasherize";
14
14
  export * from "./HasUppercase";
15
+ export * from "./EnsureLeading";
16
+ export * from "./EnsureTrailing";
15
17
  export * from "./IsCapitalized";
16
18
  export * from "./KebabCase";
17
19
  export * from "./LowerAllCaps";
18
20
  export * from "./PascalCase";
21
+ export * from "./PathJoin";
19
22
  export * from "./Pluralize";
20
23
  export * from "./SnakeCase";
21
- export * from "./StripStarting";
22
- export * from "./StripEnding";
24
+ export * from "./StripLeading";
25
+ export * from "./StripTrailing";
23
26
  export * from "./alpha-characters";
24
27
  export * from "./Url";
25
28
 
@@ -0,0 +1,21 @@
1
+ import { AnyFunction } from "src/runtime";
2
+ import { Length } from "../Length";
3
+ import { Narrowable } from "../Narrowable";
4
+ import { IsEqual } from "./equivalency";
5
+
6
+ /**
7
+ * **HasParameters**`<T>`
8
+ *
9
+ * Type utility which detects if `T` is both a function and whether that
10
+ * function has at least one type parameter.
11
+ * ```ts
12
+ * const fn = (foo: string) => `${foo}bar`;
13
+ * // true
14
+ * type P = HasParameters<typeof fn>;
15
+ * ```
16
+ */
17
+ export type HasParameters<T extends Narrowable> = T extends AnyFunction
18
+ ? IsEqual<Length<Parameters<T>>, 0> extends true
19
+ ? false
20
+ : true
21
+ : false;
@@ -1,4 +1,4 @@
1
- import { Equal } from "./equivalency";
1
+ import { IsEqual } from "./equivalency";
2
2
  import { AfterFirst, First } from "../lists";
3
3
  import { Narrowable } from "../Narrowable";
4
4
 
@@ -47,7 +47,7 @@ export type Contains<T extends Narrowable, A extends readonly any[]> = First<A>
47
47
  * `true` if `T` _extends_ any element in `A` making it match widely against `A`. If you
48
48
  * prefer a wider match you can use `Contains<T,A>` instead.
49
49
  */
50
- export type NarrowlyContains<T extends Narrowable, A extends readonly any[]> = Equal<
50
+ export type NarrowlyContains<T extends Narrowable, A extends readonly any[]> = IsEqual<
51
51
  First<A>,
52
52
  T
53
53
  > extends true
@@ -1,6 +1,6 @@
1
1
  import { Narrowable } from "../Narrowable";
2
2
 
3
- export type IsBoolean<T> = T extends boolean ? true : false;
3
+ export type IsBoolean<T> = [T] extends [boolean] ? true : false;
4
4
 
5
5
  /**
6
6
  * Type utility which returns `true` or `false` based on
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Type utility which tests whether two types -- `X` and `Y` -- are exactly the same type
5
5
  */
6
- export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
6
+ export type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
7
7
  ? true
8
8
  : false;
9
9
 
@@ -12,4 +12,4 @@ export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T exte
12
12
  *
13
13
  * Type utility which tests whether two types -- `X` and `Y` -- are _not_ exactly the same type
14
14
  */
15
- export type NotEqual<X, Y> = true extends Equal<X, Y> ? false : true;
15
+ export type NotEqual<X, Y> = true extends IsEqual<X, Y> ? false : true;
@@ -5,6 +5,7 @@ export * from "./IsScalar";
5
5
  export * from "./IsLiteral";
6
6
  export * from "./IsUndefined";
7
7
  export * from "./Extends";
8
+ export * from "./HasParameters";
8
9
  export * from "./TypeDefault";
9
10
  export * from "./object";
10
11
  export * from "./StartsWith";
@@ -128,6 +128,11 @@ export type WithValue<
128
128
  : // Exclude using E
129
129
  Omit<Pick<T, KeysWithValue<W, T>>, KeysWithValue<E, T>>;
130
130
 
131
+ export type DictionaryWithoutValue<TDict extends object, TWithoutValue> = Omit<
132
+ TDict,
133
+ KeysWithValue<TWithoutValue, TDict>
134
+ >;
135
+
131
136
  /**
132
137
  * Reduces an object to only the key/value pairs where the key is a
133
138
  * string.
@@ -0,0 +1,151 @@
1
+ import { TupleToUnion, UnionToTuple } from "../type-conversion";
2
+ import { Narrowable } from "../Narrowable";
3
+ import { First } from "./First";
4
+ import { AfterFirst } from "./AfterFirst";
5
+ import { Keys } from "../Keys";
6
+
7
+ import { DictionaryWithoutValue } from "../dictionary/props";
8
+
9
+ // [Mapped Tuple Types](https://github.com/Microsoft/TypeScript/issues/25947)
10
+
11
+ /**
12
+ * The basic shape of a `Converter`
13
+ */
14
+ export type ConverterShape<
15
+ S extends Narrowable,
16
+ N extends Narrowable,
17
+ B extends Narrowable,
18
+ O extends Narrowable
19
+ > = {
20
+ string: <T extends string>(v: T) => S;
21
+ number: <T extends number>(v: T) => N;
22
+ boolean: <T extends boolean>(v: T) => B;
23
+ object: <T extends Record<string, any>>(v: T) => O;
24
+ };
25
+
26
+ type ConverterKeys<S, N, B, O> = UnionToTuple<
27
+ Keys<
28
+ DictionaryWithoutValue<
29
+ {
30
+ string: S;
31
+ number: N;
32
+ boolean: B;
33
+ object: O;
34
+ },
35
+ undefined
36
+ >
37
+ >
38
+ >;
39
+
40
+ type ConverterInputType<T extends string> = T extends "string"
41
+ ? string
42
+ : T extends "number"
43
+ ? number
44
+ : T extends "boolean"
45
+ ? boolean
46
+ : T extends "object"
47
+ ? Record<string, any>
48
+ : unknown;
49
+
50
+ type ConverterInputUnion<
51
+ TConverted extends readonly any[],
52
+ TRemaining extends readonly string[]
53
+ > = [] extends TRemaining
54
+ ? // we're done iterating
55
+ TConverted
56
+ : // recurse
57
+ ConverterInputUnion<
58
+ [...TConverted, ConverterInputType<First<TRemaining>>],
59
+ AfterFirst<TRemaining>
60
+ >;
61
+
62
+ /**
63
+ * **AvailableConverters**
64
+ *
65
+ * Type utility which will produce the correct union type for a "converter"
66
+ */
67
+ export type AvailableConverters<S, N, B, O> = ConverterKeys<S, N, B, O> extends readonly string[]
68
+ ? TupleToUnion<ConverterInputUnion<[], ConverterKeys<S, N, B, O>>>
69
+ : never;
70
+
71
+ /**
72
+ * **Converter**
73
+ *
74
+ * A type converter coming from the `createConverter()` utility. It receives
75
+ * one or more broad types (e.g., number, string, etc.) -- what it can take
76
+ * is based on what is configured -- and then converts based on the value
77
+ * passed in.
78
+ *
79
+ * The primary goal is to preserve as many _narrow_ types as possible in this process.
80
+ */
81
+ export type Converter<
82
+ S extends Narrowable,
83
+ N extends Narrowable,
84
+ B extends Narrowable,
85
+ O extends Narrowable
86
+ > = <T extends AvailableConverters<S, N, B, O>>(input: T) => ConverterShape<S, N, B, O>;
87
+
88
+ export type Conversion<TInput extends Narrowable, TOutput extends Narrowable> = <T extends TInput>(
89
+ input: T
90
+ ) => TOutput;
91
+
92
+ export type ConverterCoverage = "string" | "number" | "boolean" | "object";
93
+
94
+ /**
95
+ * Extracts the coverage provided by a `StrongMap` as a Tuple
96
+ */
97
+ export type MapCoverage<T extends StrongMap<ConverterCoverage>> = T extends StrongMap<
98
+ infer Coverage
99
+ >
100
+ ? UnionToTuple<Coverage>
101
+ : never;
102
+
103
+ export type StrongMapTypes<K extends readonly any[]> = [] extends K
104
+ ? never
105
+ : TupleToUnion<
106
+ [
107
+ First<K> extends "string"
108
+ ? TupleToUnion<[string, ...UnionToTuple<StrongMapTypes<AfterFirst<K>>>]>
109
+ : First<K> extends "number"
110
+ ? TupleToUnion<[number, ...UnionToTuple<StrongMapTypes<AfterFirst<K>>>]>
111
+ : First<K> extends "boolean"
112
+ ? TupleToUnion<[boolean, ...UnionToTuple<StrongMapTypes<AfterFirst<K>>>]>
113
+ : First<K> extends "object"
114
+ ? TupleToUnion<[Record<string, any>, ...UnionToTuple<StrongMapTypes<AfterFirst<K>>>]>
115
+ : never,
116
+ ...UnionToTuple<StrongMapTypes<AfterFirst<K>>>
117
+ ]
118
+ >;
119
+
120
+ export type StrongMap<TDefined = ConverterCoverage> = {
121
+ string: TDefined extends "string" ? <T extends string, R extends any>(v: T) => R : never;
122
+ number: TDefined extends "number" ? <T extends number, R extends any>(v: T) => R : never;
123
+ boolean: TDefined extends "boolean" ? <T extends boolean, R extends any>(v: T) => R : never;
124
+ object: TDefined extends "object"
125
+ ? <T extends Record<string, any>, R extends any>(v: T) => R
126
+ : never;
127
+ };
128
+
129
+ export type StrongMapTransformer<M extends StrongMap> = <
130
+ T extends readonly StrongMapTypes<MapCoverage<M>>[]
131
+ >(
132
+ tuple: T
133
+ ) => {
134
+ [K in keyof T]: T[K] extends string
135
+ ? M["string"] extends (...args: any[]) => any
136
+ ? ReturnType<M["string"]>
137
+ : never
138
+ : T[K] extends number
139
+ ? M["number"] extends (...args: any[]) => any
140
+ ? ReturnType<M["number"]>
141
+ : never
142
+ : T[K] extends boolean
143
+ ? M["boolean"] extends (...args: any[]) => any
144
+ ? ReturnType<M["boolean"]>
145
+ : never
146
+ : T[K] extends Record<string, any>
147
+ ? M["object"] extends (...args: any[]) => any
148
+ ? ReturnType<M["object"]>
149
+ : never
150
+ : never;
151
+ }[keyof T];
@@ -1,7 +1,22 @@
1
+ import { AnyFunction } from "src/runtime";
2
+ import { TupleToUnion } from "./TupleToUnion";
3
+
1
4
  export type Widen<T> = T extends string
2
5
  ? string
3
6
  : T extends number
4
7
  ? number
5
8
  : T extends boolean
6
9
  ? boolean
10
+ : T extends readonly string[]
11
+ ? string[]
12
+ : T extends readonly number[]
13
+ ? number[]
14
+ : T extends readonly boolean[]
15
+ ? boolean[]
16
+ : T extends readonly AnyFunction[]
17
+ ? AnyFunction[]
18
+ : T extends readonly any[]
19
+ ? TupleToUnion<T>[]
20
+ : T extends {}
21
+ ? {}
7
22
  : T;
@@ -0,0 +1,29 @@
1
+ import { Equal, Expect } from "@type-challenges/utils";
2
+ import { HasParameters } from "src/types";
3
+ import { describe, it } from "vitest";
4
+
5
+ // Note: type tests fail visible inspection but pass from Vitest
6
+ // standpoint so always be sure to run `tsc --noEmit` over your test files to
7
+ // gain validation that no new type vulnerabilities have cropped up.
8
+
9
+ describe("HasParameters<T>", () => {
10
+ it("happy path", () => {
11
+ const fn1 = (foo: string) => `${foo}bar`;
12
+ const fn2 = (foo: string) => `${foo}bar` as const;
13
+ const fn3 = () => `hello world`;
14
+ const fn4 = () => `hello world` as const;
15
+ const fn5 = (r: string, g: string, b: string) => `${r},${g},${b}` as const;
16
+
17
+ type cases = [
18
+ // single
19
+ Expect<Equal<HasParameters<typeof fn1>, true>>,
20
+ Expect<Equal<HasParameters<typeof fn2>, true>>,
21
+ // none
22
+ Expect<Equal<HasParameters<typeof fn3>, false>>,
23
+ Expect<Equal<HasParameters<typeof fn4>, false>>,
24
+ // multiple
25
+ Expect<Equal<HasParameters<typeof fn5>, true>>
26
+ ];
27
+ const cases: cases = [true, true, true, true, true];
28
+ });
29
+ });
@@ -1,7 +1,25 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { asArray } from "src/runtime";
2
+ import { AsArray, asArray } from "src/runtime";
3
3
  import { Equal, Expect } from "@type-challenges/utils";
4
4
 
5
+ describe("AsArray<T>", () => {
6
+ it("happy path", () => {
7
+ type T1 = AsArray<4>;
8
+ type T2 = AsArray<4, true>;
9
+ type T3 = AsArray<[4, 5, 6]>;
10
+ type T4 = AsArray<[4, 5, 6], true>;
11
+
12
+ type cases = [
13
+ //
14
+ Expect<Equal<T1, 4[]>>,
15
+ Expect<Equal<T2, number[]>>,
16
+ Expect<Equal<T3, [4, 5, 6]>>,
17
+ Expect<Equal<T4, number[]>>
18
+ ];
19
+ const cases: cases = [true, true, true, true];
20
+ });
21
+ });
22
+
5
23
  describe("asArray() function", () => {
6
24
  it("non-array is returned as an array", () => {
7
25
  const i = "a";
@@ -0,0 +1,79 @@
1
+ import { Equal, Expect } from "@type-challenges/utils";
2
+ import { ensureLeading } from "src/runtime/literals/ensureLeading";
3
+ import { ensureTrailing } from "src/runtime/literals/ensureTrailing";
4
+ import { stripLeading } from "src/runtime/literals/stripLeading";
5
+ import { stripTrailing } from "src/runtime/literals/stripTrailing";
6
+ import { EnsureLeading, EnsureTrailing, StripLeading, StripTrailing } from "src/types/alphabetic";
7
+ import { describe, expect, it } from "vitest";
8
+
9
+ // Note: while type tests clearly fail visible inspection, they pass from Vitest
10
+ // standpoint so always be sure to run `tsc --noEmit` over your test files to
11
+ // gain validation that no new type vulnerabilities have cropped up.
12
+
13
+ describe("EnsureLeading", () => {
14
+ it("EnsureLeading<T,U>", () => {
15
+ type T1 = EnsureLeading<"bar", "foo">;
16
+ type T2 = EnsureLeading<"foobar", "foo">;
17
+
18
+ type cases = [Expect<Equal<T1, "foobar">>, Expect<Equal<T2, "foobar">>];
19
+ const cases: cases = [true, true];
20
+ });
21
+
22
+ it("ensureLeading()", () => {
23
+ const t1 = ensureLeading("bar", "foo");
24
+ const t2 = ensureLeading("foobar", "foo");
25
+ expect(t1).toBe("foobar");
26
+ expect(t2).toBe("foobar");
27
+ });
28
+ });
29
+
30
+ describe("EnsureTrailing", () => {
31
+ it("EnsureTrailing<T,U>", () => {
32
+ type T1 = EnsureTrailing<"foo", "bar">;
33
+ type T2 = EnsureTrailing<"foobar", "bar">;
34
+
35
+ type cases = [Expect<Equal<T1, "foobar">>, Expect<Equal<T2, "foobar">>];
36
+ const cases: cases = [true, true];
37
+ });
38
+
39
+ it("ensureTrailing()", () => {
40
+ const t1 = ensureTrailing("foo", "bar");
41
+ const t2 = ensureTrailing("foobar", "bar");
42
+ expect(t1).toBe("foobar");
43
+ expect(t2).toBe("foobar");
44
+ });
45
+ });
46
+
47
+ describe("StripLeading", () => {
48
+ it("StripLeading<T,U>", () => {
49
+ type T1 = StripLeading<"foobar", "foo">;
50
+ type T2 = StripLeading<"bar", "foo">;
51
+
52
+ type cases = [Expect<Equal<T1, "bar">>, Expect<Equal<T2, "bar">>];
53
+ const cases: cases = [true, true];
54
+ });
55
+
56
+ it("stripLeading()", () => {
57
+ const t1 = stripLeading("foobar", "foo");
58
+ const t2 = stripLeading("bar", "foo");
59
+ expect(t1).toBe("bar");
60
+ expect(t2).toBe("bar");
61
+ });
62
+ });
63
+
64
+ describe("StripTrailing", () => {
65
+ it("StripTrailing<T,U>", () => {
66
+ type T1 = StripTrailing<"foobar", "bar">;
67
+ type T2 = StripTrailing<"foo", "bar">;
68
+
69
+ type cases = [Expect<Equal<T1, "foo">>, Expect<Equal<T2, "foo">>];
70
+ const cases: cases = [true, true];
71
+ });
72
+
73
+ it("stripTrailing()", () => {
74
+ const t1 = stripTrailing("foobar", "bar");
75
+ const t2 = stripTrailing("foo", "bar");
76
+ expect(t1).toBe("foo");
77
+ expect(t2).toBe("foo");
78
+ });
79
+ });