inferred-types 0.34.0 → 0.35.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 (38) hide show
  1. package/dist/index.d.ts +89 -39
  2. package/dist/index.js +16 -0
  3. package/dist/index.mjs +14 -0
  4. package/package.json +8 -8
  5. package/src/runtime/combinators/or.ts +1 -1
  6. package/src/runtime/literals/index.ts +2 -0
  7. package/src/runtime/literals/pathJoin.ts +17 -0
  8. package/src/runtime/literals/stripEnding.ts +9 -0
  9. package/src/runtime/literals/stripStarting.ts +15 -0
  10. package/src/runtime/type-checks/isFalse.ts +1 -1
  11. package/src/runtime/type-checks/isTrue.ts +2 -3
  12. package/src/runtime/type-checks/startsWith.ts +7 -9
  13. package/src/types/alphabetic/PathJoin.ts +40 -0
  14. package/src/types/alphabetic/StripEnding.ts +23 -0
  15. package/src/types/alphabetic/StripStarting.ts +23 -0
  16. package/src/types/alphabetic/index.ts +2 -0
  17. package/src/types/boolean-logic/EndsWith.ts +30 -12
  18. package/src/types/boolean-logic/Extends.ts +11 -2
  19. package/src/types/boolean-logic/Includes.ts +3 -4
  20. package/src/types/boolean-logic/IsLiteral.ts +51 -4
  21. package/src/types/boolean-logic/IsScalar.ts +10 -4
  22. package/src/types/boolean-logic/IsUndefined.ts +5 -1
  23. package/src/types/{combinators → boolean-logic}/Or.ts +1 -1
  24. package/src/types/boolean-logic/StartsWith.ts +12 -13
  25. package/src/types/boolean-logic/array.ts +1 -1
  26. package/src/types/boolean-logic/boolean.ts +22 -14
  27. package/src/types/{combinators → boolean-logic}/equivalency.ts +0 -0
  28. package/src/types/boolean-logic/index.ts +2 -2
  29. package/src/types/boolean-logic/object.ts +4 -1
  30. package/src/types/boolean-logic/string.ts +1 -1
  31. package/src/types/index.ts +0 -1
  32. package/src/types/lists/FilterTuple.ts +20 -0
  33. package/src/types/lists/index.ts +1 -0
  34. package/tests/literals/PathJoin.test.ts +87 -0
  35. package/tests/runtime/if-is.spec.ts +11 -4
  36. package/src/types/boolean-logic/IsNumericLiteral.ts +0 -16
  37. package/src/types/boolean-logic/IsStringLiteral.ts +0 -14
  38. package/src/types/combinators/index.ts +0 -2
package/dist/index.d.ts CHANGED
@@ -166,6 +166,19 @@ type AfterFirst<T extends readonly any[]> = T extends readonly [any, ...any[]] ?
166
166
  */
167
167
  type FirstString<T> = T extends [infer S extends string, ...unknown[]] ? S : never;
168
168
 
169
+ /**
170
+ * **FilterTuple**
171
+ *
172
+ * Allows a known tuple `T` to be _filtered down_ by eliminating all items
173
+ * in the Tuple that _extend_ type `F`
174
+ * ```ts
175
+ * type T = [1,"foo",3];
176
+ * // [1,3]
177
+ * type T2 = FilterTuple<T, string>;
178
+ * ```
179
+ */
180
+ type FilterTuple<TTuple extends any[] | readonly any[], TFilter, Result extends any[] = []> = TTuple extends [infer A, ...infer R] ? [A] extends [TFilter] ? FilterTuple<R, TFilter, Result> : FilterTuple<R, TFilter, [...Result, A]> : Result;
181
+
169
182
  /**
170
183
  * **Split**`<T, SEP>`
171
184
  *
@@ -280,26 +293,26 @@ type IsFalse<T extends Narrowable> = IsBoolean<T> extends true ? T extends false
280
293
  * to the IF, ELSE, or MAYBE generic types passed in where _maybe_ is when T
281
294
  * is the wide type of `boolean`
282
295
  */
283
- type IfTrue<T, IF, ELSE, MAYBE> = IsTrue<T> extends true ? IF : IsTrue<T> extends false ? ELSE : MAYBE;
296
+ type IfTrue<T extends boolean, IF extends Narrowable, ELSE extends Narrowable, MAYBE extends Narrowable> = IsTrue<T> extends true ? IF : IsTrue<T> extends false ? ELSE : MAYBE;
284
297
  /**
285
298
  * Type utility which checks for literal `false` value and then switches type
286
299
  * to the IF, ELSE, or MAYBE generic types passed in where _maybe_ is when T
287
300
  * is the wide type of `boolean`
288
301
  */
289
- type IfFalse<T, IF, ELSE, MAYBE> = IsFalse<T> extends true ? IF : IsTrue<T> extends false ? ELSE : MAYBE;
302
+ type IfFalse<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable, MAYBE extends Narrowable> = IsFalse<T> extends true ? IF : IsTrue<T> extends false ? ELSE : MAYBE;
290
303
  /**
291
304
  * **IsBooleanLiteral**
292
305
  *
293
306
  * Type utility which returns true/false if the boolean value is a _boolean literal_ versus
294
307
  * just the wider _boolean_ type.
295
308
  */
296
- type IsBooleanLiteral<T extends boolean> = boolean extends T ? false : true;
309
+ type IsBooleanLiteral<T extends Narrowable> = IsTrue<T> extends true ? true : IsFalse<T> extends true ? true : false;
297
310
  /**
298
311
  * **IfBooleanLiteral**
299
312
  *
300
313
  * Branch utility which returns `IF` type when `T` is a boolean literal and `ELSE` otherwise
301
314
  */
302
- type IfBooleanLiteral<T extends boolean, IF, ELSE> = IsBooleanLiteral<T> extends true ? IF : ELSE;
315
+ type IfBooleanLiteral<T extends boolean, IF extends Narrowable, ELSE extends Narrowable> = IsBooleanLiteral<T> extends true ? IF : ELSE;
303
316
 
304
317
  /**
305
318
  * **IsStringLiteral**
@@ -307,14 +320,15 @@ type IfBooleanLiteral<T extends boolean, IF, ELSE> = IsBooleanLiteral<T> extends
307
320
  * Type utility which returns true/false if the string a _string literal_ versus
308
321
  * just the _string_ type.
309
322
  */
310
- type IsStringLiteral<T> = [T] extends [string] ? (string extends T ? false : true) : false;
323
+ type IsStringLiteral<T extends Narrowable> = [T] extends [string] ? string extends T ? false : true : false;
311
324
  /**
312
325
  * **IfStringLiteral**
313
326
  *
314
327
  * Branch utility which returns `IF` type when `T` is a string literal and `ELSE` otherwise
315
328
  */
316
- type IfStringLiteral<T, IF, ELSE> = [IsStringLiteral<T>] extends [true] ? IF : ELSE;
317
-
329
+ type IfStringLiteral<T extends string, IF extends Narrowable, ELSE extends Narrowable> = [
330
+ IsStringLiteral<T>
331
+ ] extends [true] ? IF : ELSE;
318
332
  /**
319
333
  * **IsNumericLiteral**
320
334
  *
@@ -327,8 +341,7 @@ type IsNumericLiteral<T extends number> = number extends T ? false : true;
327
341
  *
328
342
  * Branch utility which returns `IF` type when `T` is a numeric literal and `ELSE` otherwise
329
343
  */
330
- type IfNumericLiteral<T extends number, IF, ELSE> = IsNumericLiteral<T> extends true ? IF : ELSE;
331
-
344
+ type IfNumericLiteral<T extends number, IF extends Narrowable, ELSE extends Narrowable> = IsNumericLiteral<T> extends true ? IF : ELSE;
332
345
  /**
333
346
  * **IsLiteral**
334
347
  *
@@ -354,17 +367,17 @@ type IsOptionalLiteral<T> = [Exclude<T, undefined>] extends [string] ? IsStringL
354
367
  *
355
368
  * Branch type utility with return `IF` when `T` is a _literal_ value and `ELSE` otherwise
356
369
  */
357
- type IfLiteral<T, IF, ELSE> = IsLiteral<T> extends true ? IF : ELSE;
370
+ type IfLiteral<T, IF extends Narrowable, ELSE extends Narrowable> = IsLiteral<T> extends true ? IF : ELSE;
358
371
  /**
359
372
  * **IfOptionalLiteral**
360
373
  *
361
374
  * Branch type utility with return `IF` when `T` is a _literal_ value (with possibly
362
375
  * the inclusion of _undefined_); otherwise returns the type `ELSE`
363
376
  */
364
- type IfOptionalLiteral<T, IF, ELSE> = IsOptionalLiteral<T> extends true ? IF : ELSE;
377
+ type IfOptionalLiteral<T, IF extends Narrowable, ELSE extends Narrowable> = IsOptionalLiteral<T> extends true ? IF : ELSE;
365
378
 
366
379
  /**
367
- * **Includes<TSource, TValue>**
380
+ * **Includes**`<TSource, TValue>`
368
381
  *
369
382
  * Type utility which returns `true` or `false` based on whether `TValue` is found
370
383
  * in `TSource`. Where `TSource` can be a string literal or an array of string literals.
@@ -373,15 +386,15 @@ type IfOptionalLiteral<T, IF, ELSE> = IsOptionalLiteral<T> extends true ? IF : E
373
386
  * no way to know at design-time whether the value includes `TValue` and so it will return
374
387
  * a type of `boolean`.
375
388
  */
376
- type Includes<TSource extends string | string[], TValue extends string> = TSource extends string[] ? IsStringLiteral<TupleToUnion<TSource>> extends true ? IsLiteral<TValue> extends true ? TValue extends TupleToUnion<TSource> ? true : false : boolean : boolean : TSource extends string ? IsLiteral<TSource> extends true ? IsLiteral<TValue> extends true ? TSource extends `${string}${TValue}${string}` ? true : false : boolean : boolean : boolean;
389
+ type Includes<TSource extends string | readonly string[], TValue extends string> = TSource extends string[] ? IsStringLiteral<TupleToUnion<TSource>> extends true ? IsLiteral<TValue> extends true ? TValue extends TupleToUnion<TSource> ? true : false : boolean : boolean : TSource extends string ? IsLiteral<TSource> extends true ? IsLiteral<TValue> extends true ? TSource extends `${string}${TValue}${string}` ? true : false : boolean : boolean : boolean;
377
390
 
378
- type IsScalar<T> = [T] extends [string] ? true : [T] extends [number] ? true : [T] extends [boolean] ? true : false;
391
+ type IsScalar<T extends Narrowable> = [T] extends [string] ? true : [T] extends [number] ? true : [T] extends [boolean] ? true : false;
379
392
  /**
380
- * **IfScalar**
393
+ * **IfScalar**`<T, IF, ELSE>`
381
394
  *
382
- * Branch type utility which returns `IF` when `T` is a scalar value and `ELSE` otherwise
395
+ * Branch type utility which returns `IF` when `T` is a scalar value (aka, string, number, or boolean) and `ELSE` otherwise
383
396
  */
384
- type IfScalar<T, IF, ELSE> = IsScalar<T> extends true ? IF : ELSE;
397
+ type IfScalar<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable> = IsScalar<T> extends true ? IF : ELSE;
385
398
 
386
399
  /**
387
400
  * **IsUndefined**
@@ -395,21 +408,21 @@ type IsUndefined<T extends Narrowable> = T extends undefined ? true : false;
395
408
  * Type utility which returns `IF` type when `T` is an _undefined_
396
409
  * otherwise returns `ELSE` type.
397
410
  */
398
- type IfUndefined<T, IF, ELSE> = IsUndefined<T> extends true ? IF : ELSE;
411
+ type IfUndefined<T, IF extends Narrowable, ELSE extends Narrowable> = IsUndefined<T> extends true ? IF : ELSE;
399
412
 
400
413
  /**
401
414
  * **Extends**`<T, EXTENDS>`
402
415
  *
403
416
  * Boolean type utility which returns `true` if `T` _extends_ `EXTENDS`.
404
417
  */
405
- type Extends<T, EXTENDS> = T extends EXTENDS ? true : false;
418
+ type Extends<T extends Narrowable, EXTENDS extends Narrowable> = T extends EXTENDS ? true : false;
406
419
  /**
407
420
  * **IfExtends**
408
421
  *
409
422
  * Branching type utility which returns type `IF` when `E` _extends_ `T`; otherwise
410
423
  * it will return the type `ELSE`.
411
424
  */
412
- type IfExtends<E, T, IF, ELSE> = Extends<E, T> extends true ? IF : ELSE;
425
+ type IfExtends<T extends Narrowable, EXTENDS extends Narrowable, IF extends Narrowable, ELSE extends Narrowable> = Extends<T, EXTENDS> extends true ? IF : ELSE;
413
426
 
414
427
  /**
415
428
  * Often when mutating the shape of an object you will end up with the union of a number of
@@ -460,7 +473,7 @@ type IsObject<T> = Mutable<T> extends Record<string, any> ? T extends FunctionTy
460
473
  * Branch type utility with return `IF` when `T` extends an object type
461
474
  * and `ELSE` otherwise
462
475
  */
463
- type IfObject<T, IF, ELSE> = IsObject<T> extends true ? IF : ELSE;
476
+ type IfObject<T, IF extends Narrowable, ELSE extends Narrowable> = IsObject<T> extends true ? IF : ELSE;
464
477
 
465
478
  /**
466
479
  * **StartsWith**<TValue, TStartsWith>
@@ -471,8 +484,7 @@ type IfObject<T, IF, ELSE> = IsObject<T> extends true ? IF : ELSE;
471
484
  * to a literal `true` or `false` but if either is not a literal that it will
472
485
  * just resolve to `boolean` as the value can not be known at design time..
473
486
  */
474
- type StartsWith<TValue extends unknown, TStartsWith extends unknown> = TValue extends string ? TStartsWith extends string ? TValue extends `${TStartsWith}${string}` ? IfStringLiteral<TValue, //
475
- IfStringLiteral<TStartsWith, true, boolean>, boolean> : IfStringLiteral<TValue, false, boolean> : false : false;
487
+ type StartsWith<TValue extends string, TStartsWith extends string> = IsStringLiteral<TStartsWith> extends true ? IsStringLiteral<TValue> extends true ? TValue extends `${TStartsWith}${string}` ? true : false : boolean : boolean;
476
488
  /**
477
489
  * **IfStartsWith**<TValue, TStartsWith, IF, ELSE, MAYBE>
478
490
  *
@@ -483,7 +495,7 @@ IfStringLiteral<TStartsWith, true, boolean>, boolean> : IfStringLiteral<TValue,
483
495
  * which can be stated for cases where TValue or TStartsWith _might_ be the wider `string`
484
496
  * type and therefore the type is unknown at design time.
485
497
  */
486
- type IfStartsWith<TValue extends unknown, TStartsWith extends unknown, IF extends Narrowable, ELSE extends Narrowable> = StartsWith<TValue, TStartsWith> extends true ? IF : StartsWith<TValue, TStartsWith> extends false ? ELSE : IF | ELSE;
498
+ type IfStartsWith<TValue extends string, TStartsWith extends string, IF extends Narrowable, ELSE extends Narrowable> = StartsWith<TValue, TStartsWith> extends true ? IF : StartsWith<TValue, TStartsWith> extends false ? ELSE : IF | ELSE;
487
499
 
488
500
  /**
489
501
  * **EndsWith**<T,U>
@@ -494,8 +506,22 @@ type IfStartsWith<TValue extends unknown, TStartsWith extends unknown, IF extend
494
506
  * to a literal `true` or `false` but if either is not a literal that it will
495
507
  * just resolve to `boolean` as the value can not be known at design time..
496
508
  */
497
- type EndsWith<T extends unknown, U extends unknown> = T extends string ? U extends string ? T extends `${string}${U}` ? IfStringLiteral<T, //
498
- IfStringLiteral<U, true, boolean>, boolean> : IfStringLiteral<T, false, boolean> : false : false;
509
+ type EndsWith<TValue extends string, TEndsWith extends string> = IsStringLiteral<TEndsWith> extends true ? IsStringLiteral<TValue> extends true ? TValue extends `${string}${TEndsWith}` ? true : false : boolean : boolean;
510
+ /**
511
+ * **IfEndsWith**<TValue, TEndsWith, IF, ELSE, MAYBE>
512
+ *
513
+ * Type utility which converts type to `IF` type _if_ `TValue` _ends with_ `TEndsWith` but
514
+ * otherwise converts type to `ELSE`. If there are wide types in the mix then the type will
515
+ * result in the union of IF and ELSE.
516
+ */
517
+ type IfEndsWith<TValue extends string, TEndsWith extends string, IF extends Narrowable, ELSE extends Narrowable> = EndsWith<TValue, TEndsWith> extends true ? IF : EndsWith<TValue, TEndsWith> extends false ? ELSE : IF | ELSE;
518
+
519
+ /**
520
+ * **Or**`<T>`
521
+ *
522
+ * Takes an array of boolean values and produces a boolean OR across these values
523
+ */
524
+ type Or<T extends readonly boolean[]> = NarrowlyContains<true, T> extends true ? true : NarrowlyContains<boolean, T> extends true ? boolean : false;
499
525
 
500
526
  /**
501
527
  * Makes a readonly structure mutable
@@ -1231,6 +1257,34 @@ type SpaceToDash<T extends string> = T extends `${infer Begin}${" "}${infer Rest
1231
1257
  * ``` */
1232
1258
  type SnakeCase<S extends string> = string extends S ? string : DashUppercase<Uncapitalize<SpaceToDash<Trim<LowerAllCaps<S>>>>> extends `${infer Begin}${"-"}${infer Rest}` ? SnakeCase<`${Lowercase<Begin>}_${Rest}`> : Lowercase<DashUppercase<Uncapitalize<Trim<LowerAllCaps<S>>>>>;
1233
1259
 
1260
+ /**
1261
+ * **StripStarting**`<T, U>`
1262
+ *
1263
+ * Will strip off of `T` the starting string defined by `U` when
1264
+ * both are string literals.
1265
+ * ```ts
1266
+ * type T = "Hello World";
1267
+ * type U = "Hello ";
1268
+ * // "World"
1269
+ * type R = StripStarting<T,U>;
1270
+ * ```
1271
+ */
1272
+ type StripStarting<T extends string, U extends string> = IfLiteral<T, string extends U ? never : T extends `${U}${infer After}` ? After : T, string>;
1273
+
1274
+ /**
1275
+ * **StripEnding**`<T, U>`
1276
+ *
1277
+ * Will strip off of `T` the ending defined by `U` when
1278
+ * both are string literals.
1279
+ * ```ts
1280
+ * type T = "Hello World";
1281
+ * type U = " World";
1282
+ * // "Hello"
1283
+ * type R = StripEnding<T,U>;
1284
+ * ```
1285
+ */
1286
+ type StripEnding<T extends string, U extends string> = IfLiteral<T, string extends U ? never : T extends `${infer Before}${U}` ? Before : T, string>;
1287
+
1234
1288
  type NetworkProtocol = "http" | "https" | "file" | "ws" | "wss";
1235
1289
  /**
1236
1290
  * A literal variant of _string_ which is meant to represent a domain name
@@ -1698,13 +1752,6 @@ type KvFrom<T extends object> = Array<{
1698
1752
  */
1699
1753
  type KvTuple<T, K extends keyof T> = [K, Record<K, T[K]>];
1700
1754
 
1701
- /**
1702
- * **Or**`<T>`
1703
- *
1704
- * Takes an array of boolean values and produces a boolean OR across these values
1705
- */
1706
- type Or<T extends readonly boolean[]> = NarrowlyContains<true, T> extends true ? true : NarrowlyContains<boolean, T> extends true ? boolean : false;
1707
-
1708
1755
  type DictArrayFilterCallback<K extends keyof T, T extends object, R extends true | false> = (key: K, value: Pick<T, K>) => R;
1709
1756
  /**
1710
1757
  * An element in a `DictArray` shaped as a two element tuple: `[key, kv]`.
@@ -2216,6 +2263,10 @@ declare function idTypeGuard<T extends {
2216
2263
  */
2217
2264
  declare function literal<N extends Narrowable, T extends Record<keyof T, N>>(obj: T): T;
2218
2265
 
2266
+ declare function stripEnding<T extends string, U extends string>(content: T, strip: U): StripEnding<T, U>;
2267
+
2268
+ declare function stripStarting<T extends string, U extends string>(content: T, strip: U): StripStarting<T, U>;
2269
+
2219
2270
  /**
2220
2271
  * **Suggest**
2221
2272
  *
@@ -2280,7 +2331,7 @@ declare function isFalse<T>(i: T): IsFalse<T>;
2280
2331
  * typed so unlike the type utility there is no "MAYBE" state but if a wide type if
2281
2332
  * encountered the _type_ will the union of `IF` and `ELSE`.
2282
2333
  */
2283
- declare function ifFalse<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable>(val: T, ifVal: IF, elseVal: ELSE): IfFalse<T, IF, ELSE, IF | ELSE>;
2334
+ declare function ifFalse<T extends boolean, IF extends Narrowable, ELSE extends Narrowable>(val: T, ifVal: IF, elseVal: ELSE): IfFalse<T, IF, ELSE, IF | ELSE>;
2284
2335
 
2285
2336
  type IsFunction<T> = T extends FunctionType ? true : false;
2286
2337
  type HybridFunction<TProps extends {}> = (<TArgs extends any[]>(...args: TArgs) => any) & TProps;
@@ -2353,7 +2404,7 @@ type IsString<T> = T extends string ? true : false;
2353
2404
  * returns `IF` type if it is, otherwise returns the type `ELSE`.
2354
2405
  */
2355
2406
  type IfString<T extends Narrowable, //
2356
- IF extends Narrowable, ELSE extends Narrowable> = IsString<T> extends true ? IF : ELSE;
2407
+ IF extends Narrowable, ELSE extends Narrowable> = IsString<T> extends true ? IF : IsString<T> extends false ? ELSE : IF | ELSE;
2357
2408
 
2358
2409
  /**
2359
2410
  * **isString**
@@ -2388,8 +2439,7 @@ declare function isTrue<T extends Narrowable>(i: T): IsTrue<T>;
2388
2439
  * **ifTrue**
2389
2440
  *
2390
2441
  * Strongly type-aware conditional statement which checks whether a value is
2391
- * a _true_ and returns one of two values (strongly typed) based on the evaluation
2392
- * of this criteria.
2442
+ * _true_.
2393
2443
  *
2394
2444
  * @param val the value being tested
2395
2445
  * @param ifVal the value (strongly typed) returned if val is _true_ value
@@ -2399,7 +2449,7 @@ declare function isTrue<T extends Narrowable>(i: T): IsTrue<T>;
2399
2449
  * typed so unlike the type utility there is no "MAYBE" state but if a wide type if
2400
2450
  * encountered the _type_ will the union of `IF` and `ELSE`.
2401
2451
  */
2402
- declare function ifTrue<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable>(val: T, ifVal: IF, elseVal: ELSE): IfTrue<T, IF, ELSE, IF | ELSE>;
2452
+ declare function ifTrue<T extends boolean, IF extends Narrowable, ELSE extends Narrowable>(val: T, ifVal: IF, elseVal: ELSE): IfTrue<T, IF, ELSE, IF | ELSE>;
2403
2453
 
2404
2454
  declare function isUndefined<T extends Narrowable>(i: T): undefined extends T ? true : false;
2405
2455
  /**
@@ -2523,4 +2573,4 @@ interface IFluentConfigurator<C> {
2523
2573
  */
2524
2574
  declare function FluentConfigurator<I>(initial?: I): IFluentConfigurator<{}>;
2525
2575
 
2526
- export { AfterFirst, AllCaps, Alpha, AlphaNumeric, AnyFunction, Api, ApiFunction, ApiValue, AppendToDictionary, AppendToObject, ArrConcat, Array$1 as Array, ArrayConverter, AsArray, AsFinalizedConfig, AssertExtends, Box, BoxValue, BoxedFnParams, BoxedReturn, Bracket, Break, CamelCase, CapitalizeWords, Cardinality, Cardinality0, Cardinality1, CardinalityExplicit, CardinalityFilter0, CardinalityFilter1, CardinalityIn, CardinalityInput, CardinalityNode, CardinalityOut, CardinalityTuple, ClosingBracket, Condition, ConditionFilter, Configurator, ConfiguredMap, Constructor, Contains, DEFAULT_MANY_TO_ONE_MAPPING, DEFAULT_ONE_TO_MANY_MAPPING, DEFAULT_ONE_TO_ONE_MAPPING, DashToSnake, DashUppercase, Dasherize, DecomposeMapConfig, DefaultManyToOneMapping, DefaultOneToManyMapping, DefaultOneToOneMapping, DefinePropertiesApi, DictArrApi, DictArray, DictArrayFilterCallback, DictArrayKv, DictChangeValue, DictFromKv, DictKvTuple, DictPartialApplication, DictPrependWithFn, DictReturnValues, DomainName, DynamicRule, DynamicRuleSet, Email, EndsWith, EnumValues, Equal, ExpandRecursively, ExpectExtends, ExplicitFunction, Extends, FilterContains, FilterDefn, FilterEnds, FilterEquals, FilterFn, FilterGreaterThan, FilterIs, FilterLessThan, FilterNotEqual, FilterStarts, FinalReturn, FinalizedMapConfig, First, FirstKey, FirstKeyValue, FirstOfEach, FirstOrUndefined, FirstString, FluentConfigurator, FluentFunction, FnShape, FromDictArray, FullyQualifiedUrl, FunctionType, GeneralDictionary, Get, HasUppercase, HybridFunction, IConfigurator, IFluentConfigurator, If, IfArray, IfBooleanLiteral, IfExtends, IfExtendsThen, IfFalse, IfLiteral, IfNumericLiteral, IfObject, IfOptionalLiteral, IfReadonlyArray, IfScalar, IfStartsWith, IfStringLiteral, IfTrue, IfUndefined, Include, Includes, IntersectingKeys, Ipv4, IsArray, IsBoolean, IsBooleanLiteral, IsCapitalized, IsFalse, IsFunction, IsLiteral, IsNull, IsNumber, IsNumericLiteral, IsObject, IsOptionalLiteral, IsReadonlyArray, IsScalar, IsStringLiteral, IsTrue, IsUndefined, KebabCase, KeyValue, KeyedRecord, Keys, KeysWithValue, KvFrom, KvTuple, LastInUnion, LeftWhitespace, Length, LogicFunction, LogicalCombinator, LowerAllCaps, LowerAlpha, ManyToMany, ManyToOne, ManyToZero, MapCard, MapCardinality, MapCardinalityFrom, MapCardinalityIllustrated, MapConfig, MapFn, MapFnInput, MapFnOutput, MapIR, MapInput, MapInputFrom, MapOR, MapOutput, MapOutputFrom, MapTo, Mapper, MapperApi, MaybeFalse, MaybeTrue, Mutable, MutableProps, NarrowBox, Narrowable, NarrowlyContains, NetworkProtocol, NonAlpha, NonNumericKeys, NonStringKeys, Not, NotEqual, NotFilter, Numeric, NumericFilter, NumericKeys, NumericString, ObjectType, OneToMany, OneToOne, OneToZero, Opaque, OpeningBracket, OptRequired, OptionalKeys, OptionalProps, Or, Parenthesis, PascalCase, Pluralize, PrivateKey, PrivateKeys, PublicKeys, Punctuation, PureFluentApi, RelativeUrl, Replace, RequireProps, RequiredKeys, RequiredProps, Retain, RightWhitespace, RuleDefinition, RuntimeProp, RuntimeType, SameKeys, SecondOfEach, SimpleFunction, SimplifyObject, SnakeCase, SpecialCharacters, Split, StartsWith, StringDelimiter, StringFilter, StringKeys, StringLength, Suggest, SuggestNumeric, ToFluent, Transformer, Trim, TrimLeft, TrimRight, TupleToUnion, Type, TypeApi, TypeDefault, TypeDefinition, TypeGuard, TypeOptions, Unbox, UndefinedArrayIsUnknown, UndefinedTreatment, UndefinedValue, UnionToIntersection, UnionToTuple, UniqueDictionary, UniqueForProp, Uniqueness, UnwrapNot, UpperAlpha, UrlBuilder, VariableName, Where, WhereNot, Whitespace, Widen, WithNumericKeys, WithStringKeys, WithValue, ZeroToMany, ZeroToOne, ZeroToZero, ZipCode, and, api, arrayToKeyLookup, arrayToObject, asArray, box, condition, createFnWithProps, defineProperties, defineType, dictArr, dictToKv, dictionaryTransform, entries, filter, filterDictArray, fnWithProps, groupBy, idLiteral, idTypeGuard, identity, ifArray, ifArrayPartial, ifBoolean, ifFalse, ifNull, ifNumber, ifString, ifTrue, ifUndefined, isArray, isBoolean, isBox, isFalse, isFunction, isNotFilter, isNull, isNumber, isNumericFilter, isObject, isString, isSymbol, isTrue, isType, isUndefined, keys, kindLiteral, kv, kvToDict, literal, mapTo, mapToDict, mapToFn, mapValues, nameLiteral, not, or, readonlyFnWithProps, ruleSet, strArrayToDict, type, typeApi, unbox, withValue };
2576
+ export { AfterFirst, AllCaps, Alpha, AlphaNumeric, AnyFunction, Api, ApiFunction, ApiValue, AppendToDictionary, AppendToObject, ArrConcat, Array$1 as Array, ArrayConverter, AsArray, AsFinalizedConfig, AssertExtends, Box, BoxValue, BoxedFnParams, BoxedReturn, Bracket, Break, CamelCase, CapitalizeWords, Cardinality, Cardinality0, Cardinality1, CardinalityExplicit, CardinalityFilter0, CardinalityFilter1, CardinalityIn, CardinalityInput, CardinalityNode, CardinalityOut, CardinalityTuple, ClosingBracket, Condition, ConditionFilter, Configurator, ConfiguredMap, Constructor, Contains, DEFAULT_MANY_TO_ONE_MAPPING, DEFAULT_ONE_TO_MANY_MAPPING, DEFAULT_ONE_TO_ONE_MAPPING, DashToSnake, DashUppercase, Dasherize, DecomposeMapConfig, DefaultManyToOneMapping, DefaultOneToManyMapping, DefaultOneToOneMapping, DefinePropertiesApi, DictArrApi, DictArray, DictArrayFilterCallback, DictArrayKv, DictChangeValue, DictFromKv, DictKvTuple, DictPartialApplication, DictPrependWithFn, DictReturnValues, DomainName, DynamicRule, DynamicRuleSet, Email, EndsWith, EnumValues, Equal, ExpandRecursively, ExpectExtends, ExplicitFunction, Extends, FilterContains, FilterDefn, FilterEnds, FilterEquals, FilterFn, FilterGreaterThan, FilterIs, FilterLessThan, FilterNotEqual, FilterStarts, FilterTuple, FinalReturn, FinalizedMapConfig, First, FirstKey, FirstKeyValue, FirstOfEach, FirstOrUndefined, FirstString, FluentConfigurator, FluentFunction, FnShape, FromDictArray, FullyQualifiedUrl, FunctionType, GeneralDictionary, Get, HasUppercase, HybridFunction, IConfigurator, IFluentConfigurator, If, IfArray, IfBooleanLiteral, IfEndsWith, IfExtends, IfExtendsThen, IfFalse, IfLiteral, IfNumericLiteral, IfObject, IfOptionalLiteral, IfReadonlyArray, IfScalar, IfStartsWith, IfStringLiteral, IfTrue, IfUndefined, Include, Includes, IntersectingKeys, Ipv4, IsArray, IsBoolean, IsBooleanLiteral, IsCapitalized, IsFalse, IsFunction, IsLiteral, IsNull, IsNumber, IsNumericLiteral, IsObject, IsOptionalLiteral, IsReadonlyArray, IsScalar, IsStringLiteral, IsTrue, IsUndefined, KebabCase, KeyValue, KeyedRecord, Keys, KeysWithValue, KvFrom, KvTuple, LastInUnion, LeftWhitespace, Length, LogicFunction, LogicalCombinator, LowerAllCaps, LowerAlpha, ManyToMany, ManyToOne, ManyToZero, MapCard, MapCardinality, MapCardinalityFrom, MapCardinalityIllustrated, MapConfig, MapFn, MapFnInput, MapFnOutput, MapIR, MapInput, MapInputFrom, MapOR, MapOutput, MapOutputFrom, MapTo, Mapper, MapperApi, MaybeFalse, MaybeTrue, Mutable, MutableProps, NarrowBox, Narrowable, NarrowlyContains, NetworkProtocol, NonAlpha, NonNumericKeys, NonStringKeys, Not, NotEqual, NotFilter, Numeric, NumericFilter, NumericKeys, NumericString, ObjectType, OneToMany, OneToOne, OneToZero, Opaque, OpeningBracket, OptRequired, OptionalKeys, OptionalProps, Or, Parenthesis, PascalCase, Pluralize, PrivateKey, PrivateKeys, PublicKeys, Punctuation, PureFluentApi, RelativeUrl, Replace, RequireProps, RequiredKeys, RequiredProps, Retain, RightWhitespace, RuleDefinition, RuntimeProp, RuntimeType, SameKeys, SecondOfEach, SimpleFunction, SimplifyObject, SnakeCase, SpecialCharacters, Split, StartsWith, StringDelimiter, StringFilter, StringKeys, StringLength, StripEnding, StripStarting, Suggest, SuggestNumeric, ToFluent, Transformer, Trim, TrimLeft, TrimRight, TupleToUnion, Type, TypeApi, TypeDefault, TypeDefinition, TypeGuard, TypeOptions, Unbox, UndefinedArrayIsUnknown, UndefinedTreatment, UndefinedValue, UnionToIntersection, UnionToTuple, UniqueDictionary, UniqueForProp, Uniqueness, UnwrapNot, UpperAlpha, UrlBuilder, VariableName, Where, WhereNot, Whitespace, Widen, WithNumericKeys, WithStringKeys, WithValue, ZeroToMany, ZeroToOne, ZeroToZero, ZipCode, and, api, arrayToKeyLookup, arrayToObject, asArray, box, condition, createFnWithProps, defineProperties, defineType, dictArr, dictToKv, dictionaryTransform, entries, filter, filterDictArray, fnWithProps, groupBy, idLiteral, idTypeGuard, identity, ifArray, ifArrayPartial, ifBoolean, ifFalse, ifNull, ifNumber, ifString, ifTrue, ifUndefined, isArray, isBoolean, isBox, isFalse, isFunction, isNotFilter, isNull, isNumber, isNumericFilter, isObject, isString, isSymbol, isTrue, isType, isUndefined, keys, kindLiteral, kv, kvToDict, literal, mapTo, mapToDict, mapToFn, mapValues, nameLiteral, not, or, readonlyFnWithProps, ruleSet, strArrayToDict, stripEnding, stripStarting, type, typeApi, unbox, withValue };
package/dist/index.js CHANGED
@@ -87,6 +87,8 @@ __export(src_exports, {
87
87
  readonlyFnWithProps: () => readonlyFnWithProps,
88
88
  ruleSet: () => ruleSet,
89
89
  strArrayToDict: () => strArrayToDict,
90
+ stripEnding: () => stripEnding,
91
+ stripStarting: () => stripStarting,
90
92
  type: () => type,
91
93
  typeApi: () => typeApi,
92
94
  unbox: () => unbox,
@@ -776,6 +778,18 @@ function idTypeGuard(_o) {
776
778
  function literal(obj) {
777
779
  return obj;
778
780
  }
781
+
782
+ // src/runtime/literals/stripEnding.ts
783
+ function stripEnding(content, strip) {
784
+ const re = new RegExp(`(.*)${strip}$`);
785
+ return content.endsWith(strip) ? content.replace(re, "$1") : content;
786
+ }
787
+
788
+ // src/runtime/literals/stripStarting.ts
789
+ function stripStarting(content, strip) {
790
+ const re = new RegExp(`^${strip}(.*)`);
791
+ return content.startsWith(strip) ? content.replace(re, "$1") : content;
792
+ }
779
793
  // Annotate the CommonJS export names for ESM import in node:
780
794
  0 && (module.exports = {
781
795
  Configurator,
@@ -845,6 +859,8 @@ function literal(obj) {
845
859
  readonlyFnWithProps,
846
860
  ruleSet,
847
861
  strArrayToDict,
862
+ stripEnding,
863
+ stripStarting,
848
864
  type,
849
865
  typeApi,
850
866
  unbox,
package/dist/index.mjs CHANGED
@@ -680,6 +680,18 @@ function idTypeGuard(_o) {
680
680
  function literal(obj) {
681
681
  return obj;
682
682
  }
683
+
684
+ // src/runtime/literals/stripEnding.ts
685
+ function stripEnding(content, strip) {
686
+ const re = new RegExp(`(.*)${strip}$`);
687
+ return content.endsWith(strip) ? content.replace(re, "$1") : content;
688
+ }
689
+
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;
694
+ }
683
695
  export {
684
696
  Configurator,
685
697
  DEFAULT_MANY_TO_ONE_MAPPING,
@@ -748,6 +760,8 @@ export {
748
760
  readonlyFnWithProps,
749
761
  ruleSet,
750
762
  strArrayToDict,
763
+ stripEnding,
764
+ stripStarting,
751
765
  type,
752
766
  typeApi,
753
767
  unbox,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inferred-types",
3
- "version": "0.34.0",
3
+ "version": "0.35.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>",
@@ -32,16 +32,16 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@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",
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",
39
39
  "bumpp": "^8.2.1",
40
40
  "common-types": "^1.33.2",
41
41
  "cross-env": "^7.0.3",
42
42
  "dd": "^0.25.4",
43
43
  "dotenv": "^16.0.3",
44
- "eslint": "^8.28.0",
44
+ "eslint": "^8.29.0",
45
45
  "eslint-config-prettier": "^8.5.0",
46
46
  "eslint-plugin-import": "^2.26.0",
47
47
  "eslint-plugin-prettier": "^4.2.1",
@@ -53,8 +53,8 @@
53
53
  "sharp": "^0.31.2",
54
54
  "tsup": "^6.5.0",
55
55
  "typescript": "^4.9.3",
56
- "vite": "^3.2.4",
57
- "vitest": "^0.25.3"
56
+ "vite": "^3.2.5",
57
+ "vitest": "^0.25.4"
58
58
  },
59
59
  "dependencies": {
60
60
  "brilliant-errors": "^0.6.1"
@@ -1,4 +1,4 @@
1
- import { Or } from "src/types/combinators/Or";
1
+ import { Or } from "src/types/boolean-logic/Or";
2
2
 
3
3
  export function or<O extends readonly boolean[]>(...conditions: O) {
4
4
  return (conditions.some((v) => v === true) ? true : false) as Or<O>;
@@ -10,6 +10,8 @@ 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
15
  export * from "./Suggest";
14
16
 
15
17
  // #endregion auto-indexed files
@@ -0,0 +1,17 @@
1
+ import { PathJoin } from "src/types/alphabetic/PathJoin";
2
+ import { stripStarting } from "./stripStarting";
3
+ import { stripEnding } from "./stripEnding";
4
+
5
+ /**
6
+ * **pathJoin**`<T,U>()`
7
+ *
8
+ * Run time utility which joins two strings together with
9
+ * the intent to have them be divided by a Posix `/` character
10
+ * appropriate for Unix file paths and URLs.
11
+ */
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, "/");
15
+
16
+ return `${start}/${end}` as PathJoin<T, U>;
17
+ }
@@ -0,0 +1,9 @@
1
+ import { StripEnding } from "src/types/alphabetic/StripEnding";
2
+
3
+ export function stripEnding<T extends string, U extends string>(
4
+ content: T,
5
+ strip: U
6
+ ): StripEnding<T, U> {
7
+ const re = new RegExp(`(.*)${strip}$`);
8
+ return (content.endsWith(strip) ? content.replace(re, "$1") : content) as StripEnding<T, U>;
9
+ }
@@ -0,0 +1,15 @@
1
+ import { StripStarting } from "src/types/alphabetic/StripStarting";
2
+
3
+ export function stripStarting<T extends string, U extends string>(
4
+ content: T,
5
+ strip: U
6
+ ): StripStarting<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 StripStarting<T, U>;
15
+ }
@@ -20,7 +20,7 @@ export function isFalse<T>(i: T) {
20
20
  * typed so unlike the type utility there is no "MAYBE" state but if a wide type if
21
21
  * encountered the _type_ will the union of `IF` and `ELSE`.
22
22
  */
23
- export function ifFalse<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable>(
23
+ export function ifFalse<T extends boolean, IF extends Narrowable, ELSE extends Narrowable>(
24
24
  val: T,
25
25
  ifVal: IF,
26
26
  elseVal: ELSE
@@ -12,8 +12,7 @@ export function isTrue<T extends Narrowable>(i: T) {
12
12
  * **ifTrue**
13
13
  *
14
14
  * Strongly type-aware conditional statement which checks whether a value is
15
- * a _true_ and returns one of two values (strongly typed) based on the evaluation
16
- * of this criteria.
15
+ * _true_.
17
16
  *
18
17
  * @param val the value being tested
19
18
  * @param ifVal the value (strongly typed) returned if val is _true_ value
@@ -23,7 +22,7 @@ export function isTrue<T extends Narrowable>(i: T) {
23
22
  * typed so unlike the type utility there is no "MAYBE" state but if a wide type if
24
23
  * encountered the _type_ will the union of `IF` and `ELSE`.
25
24
  */
26
- export function ifTrue<T extends Narrowable, IF extends Narrowable, ELSE extends Narrowable>(
25
+ export function ifTrue<T extends boolean, IF extends Narrowable, ELSE extends Narrowable>(
27
26
  val: T,
28
27
  ifVal: IF,
29
28
  elseVal: ELSE
@@ -1,3 +1,4 @@
1
+ import { Narrowable } from "src/types";
1
2
  import { IfStartsWith, StartsWith } from "src/types/boolean-logic";
2
3
  import { IfUndefined } from "src/types/boolean-logic/IsUndefined";
3
4
  import { createFnWithProps } from "../createFnWithProps";
@@ -88,22 +89,19 @@ export type StringLiteralFn<S extends string = string> = <T extends S>(input: T)
88
89
  * returns is able to
89
90
  */
90
91
  export const ifStartsWith =
91
- <
92
- TStartsWith extends string,
93
- TIf extends <T extends string>(input: T) => any,
94
- TElse extends <T extends string>(input: T) => any
95
- >(
92
+ <TStartsWith extends string, TIf extends Narrowable, TElse extends Narrowable>(
96
93
  /** the string literal _start value_ which a string must begin with */
97
94
  start: TStartsWith,
98
95
  /** a mutation function when a value _does_ start with `TStartsWith` */
99
- isTrue: TIf,
96
+ isTrue: <T extends string>(input: T) => TIf,
100
97
  /** an optional mutation function */
101
- isFalse: TElse
98
+ isFalse: <T extends string>(input: T) => TElse
102
99
  ) =>
103
- <I extends string>(input: I) =>
100
+ <TTextValue extends string>(input: TTextValue) =>
104
101
  ifTrue(
105
102
  // condition
106
103
  startsWith(start)(input),
104
+ // handlers
107
105
  isTrue(input),
108
106
  isFalse(input)
109
- ) as IfStartsWith<I, TStartsWith, ReturnType<TIf>, ReturnType<TElse>>;
107
+ );
@@ -0,0 +1,40 @@
1
+ import { IfLiteral } from "../boolean-logic";
2
+ import { StripEnding } from "./StripEnding";
3
+ import { StripStarting } from "./StripStarting";
4
+ /**
5
+ * **PathJoin**`<T,U>`
6
+ *
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).
10
+ *
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}`.
23
+ */
24
+ export type PathJoin<
25
+ // leading string
26
+ 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
+ >;
@@ -0,0 +1,23 @@
1
+ import { IfLiteral } from "../boolean-logic/IsLiteral";
2
+
3
+ /**
4
+ * **StripEnding**`<T, U>`
5
+ *
6
+ * Will strip off of `T` the ending defined by `U` when
7
+ * both are string literals.
8
+ * ```ts
9
+ * type T = "Hello World";
10
+ * type U = " World";
11
+ * // "Hello"
12
+ * type R = StripEnding<T,U>;
13
+ * ```
14
+ */
15
+ export type StripEnding<T extends string, U extends string> = IfLiteral<
16
+ // can only operate on literal strings
17
+ T,
18
+ // this path represents successful strip opp
19
+ // but we must never accept `U` being wide
20
+ string extends U ? never : T extends `${infer Before}${U}` ? Before : T,
21
+ // here we must stay wide
22
+ string
23
+ >;
@@ -0,0 +1,23 @@
1
+ import { IfLiteral } from "../boolean-logic/IsLiteral";
2
+
3
+ /**
4
+ * **StripStarting**`<T, U>`
5
+ *
6
+ * Will strip off of `T` the starting string defined by `U` when
7
+ * both are string literals.
8
+ * ```ts
9
+ * type T = "Hello World";
10
+ * type U = "Hello ";
11
+ * // "World"
12
+ * type R = StripStarting<T,U>;
13
+ * ```
14
+ */
15
+ export type StripStarting<T extends string, U extends string> = IfLiteral<
16
+ // can only operate on literal strings
17
+ T,
18
+ // this path represents successful strip opp
19
+ // but we must never accept `U` being wide
20
+ string extends U ? never : T extends `${U}${infer After}` ? After : T,
21
+ // here we must stay wide
22
+ string
23
+ >;
@@ -18,6 +18,8 @@ export * from "./LowerAllCaps";
18
18
  export * from "./PascalCase";
19
19
  export * from "./Pluralize";
20
20
  export * from "./SnakeCase";
21
+ export * from "./StripStarting";
22
+ export * from "./StripEnding";
21
23
  export * from "./alpha-characters";
22
24
  export * from "./Url";
23
25
 
@@ -1,4 +1,5 @@
1
- import { IfStringLiteral } from "src/types/boolean-logic";
1
+ import { IsStringLiteral } from "src/types/boolean-logic";
2
+ import { Narrowable } from "../Narrowable";
2
3
 
3
4
  /**
4
5
  * **EndsWith**<T,U>
@@ -9,14 +10,31 @@ import { IfStringLiteral } from "src/types/boolean-logic";
9
10
  * to a literal `true` or `false` but if either is not a literal that it will
10
11
  * just resolve to `boolean` as the value can not be known at design time..
11
12
  */
12
- export type EndsWith<T extends unknown, U extends unknown> = T extends string
13
- ? U extends string
14
- ? T extends `${string}${U}`
15
- ? IfStringLiteral<
16
- T, //
17
- IfStringLiteral<U, true, boolean>,
18
- boolean
19
- >
20
- : IfStringLiteral<T, false, boolean>
21
- : false
22
- : false;
13
+ export type EndsWith<
14
+ TValue extends string,
15
+ TEndsWith extends string
16
+ > = IsStringLiteral<TEndsWith> extends true
17
+ ? IsStringLiteral<TValue> extends true // both literals
18
+ ? TValue extends `${string}${TEndsWith}`
19
+ ? true
20
+ : false
21
+ : boolean
22
+ : boolean;
23
+
24
+ /**
25
+ * **IfEndsWith**<TValue, TEndsWith, IF, ELSE, MAYBE>
26
+ *
27
+ * Type utility which converts type to `IF` type _if_ `TValue` _ends with_ `TEndsWith` but
28
+ * otherwise converts type to `ELSE`. If there are wide types in the mix then the type will
29
+ * result in the union of IF and ELSE.
30
+ */
31
+ export type IfEndsWith<
32
+ TValue extends string,
33
+ TEndsWith extends string,
34
+ IF extends Narrowable,
35
+ ELSE extends Narrowable
36
+ > = EndsWith<TValue, TEndsWith> extends true
37
+ ? IF
38
+ : EndsWith<TValue, TEndsWith> extends false
39
+ ? ELSE
40
+ : IF | ELSE;
@@ -1,13 +1,22 @@
1
+ import { Narrowable } from "../Narrowable";
2
+
1
3
  /**
2
4
  * **Extends**`<T, EXTENDS>`
3
5
  *
4
6
  * Boolean type utility which returns `true` if `T` _extends_ `EXTENDS`.
5
7
  */
6
- export type Extends<T, EXTENDS> = T extends EXTENDS ? true : false;
8
+ export type Extends<T extends Narrowable, EXTENDS extends Narrowable> = T extends EXTENDS
9
+ ? true
10
+ : false;
7
11
  /**
8
12
  * **IfExtends**
9
13
  *
10
14
  * Branching type utility which returns type `IF` when `E` _extends_ `T`; otherwise
11
15
  * it will return the type `ELSE`.
12
16
  */
13
- export type IfExtends<E, T, IF, ELSE> = Extends<E, T> extends true ? IF : ELSE;
17
+ export type IfExtends<
18
+ T extends Narrowable,
19
+ EXTENDS extends Narrowable,
20
+ IF extends Narrowable,
21
+ ELSE extends Narrowable
22
+ > = Extends<T, EXTENDS> extends true ? IF : ELSE;
@@ -1,9 +1,8 @@
1
1
  import { TupleToUnion } from "../type-conversion";
2
- import { IsLiteral } from "./IsLiteral";
3
- import { IsStringLiteral } from "./IsStringLiteral";
2
+ import { IsLiteral, IsStringLiteral } from "./IsLiteral";
4
3
 
5
4
  /**
6
- * **Includes<TSource, TValue>**
5
+ * **Includes**`<TSource, TValue>`
7
6
  *
8
7
  * Type utility which returns `true` or `false` based on whether `TValue` is found
9
8
  * in `TSource`. Where `TSource` can be a string literal or an array of string literals.
@@ -13,7 +12,7 @@ import { IsStringLiteral } from "./IsStringLiteral";
13
12
  * a type of `boolean`.
14
13
  */
15
14
  export type Includes<
16
- TSource extends string | string[],
15
+ TSource extends string | readonly string[],
17
16
  TValue extends string
18
17
  > = TSource extends string[]
19
18
  ? IsStringLiteral<TupleToUnion<TSource>> extends true
@@ -1,6 +1,47 @@
1
+ import { Narrowable } from "../Narrowable";
1
2
  import { IsBooleanLiteral } from "./boolean";
2
- import { IsStringLiteral } from "./IsStringLiteral";
3
- import { IsNumericLiteral } from "./IsNumericLiteral";
3
+
4
+ /**
5
+ * **IsStringLiteral**
6
+ *
7
+ * Type utility which returns true/false if the string a _string literal_ versus
8
+ * just the _string_ type.
9
+ */
10
+ export type IsStringLiteral<T extends Narrowable> = [T] extends [string]
11
+ ? string extends T
12
+ ? false
13
+ : true
14
+ : false;
15
+
16
+ /**
17
+ * **IfStringLiteral**
18
+ *
19
+ * Branch utility which returns `IF` type when `T` is a string literal and `ELSE` otherwise
20
+ */
21
+ export type IfStringLiteral<T extends string, IF extends Narrowable, ELSE extends Narrowable> = [
22
+ IsStringLiteral<T>
23
+ ] extends [true]
24
+ ? IF
25
+ : ELSE;
26
+
27
+ /**
28
+ * **IsNumericLiteral**
29
+ *
30
+ * Type utility which returns true/false if the numeric value a _numeric literal_ versus
31
+ * just the _number_ type.
32
+ */
33
+ export type IsNumericLiteral<T extends number> = number extends T ? false : true;
34
+
35
+ /**
36
+ * **IfNumericLiteral**
37
+ *
38
+ * Branch utility which returns `IF` type when `T` is a numeric literal and `ELSE` otherwise
39
+ */
40
+ export type IfNumericLiteral<
41
+ T extends number,
42
+ IF extends Narrowable,
43
+ ELSE extends Narrowable
44
+ > = IsNumericLiteral<T> extends true ? IF : ELSE;
4
45
 
5
46
  // [note on handling of boolean](https://stackoverflow.com/questions/74213646/detecting-type-literals-works-in-isolation-but-not-when-combined-with-other-lite/74213713#74213713)
6
47
 
@@ -43,7 +84,9 @@ export type IsOptionalLiteral<T> = [Exclude<T, undefined>] extends [string]
43
84
  *
44
85
  * Branch type utility with return `IF` when `T` is a _literal_ value and `ELSE` otherwise
45
86
  */
46
- export type IfLiteral<T, IF, ELSE> = IsLiteral<T> extends true ? IF : ELSE;
87
+ export type IfLiteral<T, IF extends Narrowable, ELSE extends Narrowable> = IsLiteral<T> extends true
88
+ ? IF
89
+ : ELSE;
47
90
 
48
91
  /**
49
92
  * **IfOptionalLiteral**
@@ -51,4 +94,8 @@ export type IfLiteral<T, IF, ELSE> = IsLiteral<T> extends true ? IF : ELSE;
51
94
  * Branch type utility with return `IF` when `T` is a _literal_ value (with possibly
52
95
  * the inclusion of _undefined_); otherwise returns the type `ELSE`
53
96
  */
54
- export type IfOptionalLiteral<T, IF, ELSE> = IsOptionalLiteral<T> extends true ? IF : ELSE;
97
+ export type IfOptionalLiteral<
98
+ T,
99
+ IF extends Narrowable,
100
+ ELSE extends Narrowable
101
+ > = IsOptionalLiteral<T> extends true ? IF : ELSE;
@@ -1,4 +1,6 @@
1
- export type IsScalar<T> = [T] extends [string]
1
+ import { Narrowable } from "../Narrowable";
2
+
3
+ export type IsScalar<T extends Narrowable> = [T] extends [string]
2
4
  ? true
3
5
  : [T] extends [number]
4
6
  ? true
@@ -7,8 +9,12 @@ export type IsScalar<T> = [T] extends [string]
7
9
  : false;
8
10
 
9
11
  /**
10
- * **IfScalar**
12
+ * **IfScalar**`<T, IF, ELSE>`
11
13
  *
12
- * Branch type utility which returns `IF` when `T` is a scalar value and `ELSE` otherwise
14
+ * Branch type utility which returns `IF` when `T` is a scalar value (aka, string, number, or boolean) and `ELSE` otherwise
13
15
  */
14
- export type IfScalar<T, IF, ELSE> = IsScalar<T> extends true ? IF : ELSE;
16
+ export type IfScalar<
17
+ T extends Narrowable,
18
+ IF extends Narrowable,
19
+ ELSE extends Narrowable
20
+ > = IsScalar<T> extends true ? IF : ELSE;
@@ -13,4 +13,8 @@ export type IsUndefined<T extends Narrowable> = T extends undefined ? true : fal
13
13
  * Type utility which returns `IF` type when `T` is an _undefined_
14
14
  * otherwise returns `ELSE` type.
15
15
  */
16
- export type IfUndefined<T, IF, ELSE> = IsUndefined<T> extends true ? IF : ELSE;
16
+ export type IfUndefined<
17
+ T,
18
+ IF extends Narrowable,
19
+ ELSE extends Narrowable
20
+ > = IsUndefined<T> extends true ? IF : ELSE;
@@ -1,4 +1,4 @@
1
- import { NarrowlyContains } from "../boolean-logic";
1
+ import { NarrowlyContains } from "./array";
2
2
 
3
3
  /**
4
4
  * **Or**`<T>`
@@ -1,4 +1,4 @@
1
- import { IfStringLiteral } from "src/types/boolean-logic";
1
+ import { IsStringLiteral } from "src/types/boolean-logic";
2
2
  import { Narrowable } from "../Narrowable";
3
3
  /**
4
4
  * **StartsWith**<TValue, TStartsWith>
@@ -9,17 +9,16 @@ import { Narrowable } from "../Narrowable";
9
9
  * to a literal `true` or `false` but if either is not a literal that it will
10
10
  * just resolve to `boolean` as the value can not be known at design time..
11
11
  */
12
- export type StartsWith<TValue extends unknown, TStartsWith extends unknown> = TValue extends string
13
- ? TStartsWith extends string
12
+ export type StartsWith<
13
+ TValue extends string,
14
+ TStartsWith extends string
15
+ > = IsStringLiteral<TStartsWith> extends true
16
+ ? IsStringLiteral<TValue> extends true // both literals
14
17
  ? TValue extends `${TStartsWith}${string}`
15
- ? IfStringLiteral<
16
- TValue, //
17
- IfStringLiteral<TStartsWith, true, boolean>,
18
- boolean
19
- >
20
- : IfStringLiteral<TValue, false, boolean>
21
- : false
22
- : false;
18
+ ? true
19
+ : false
20
+ : boolean
21
+ : boolean;
23
22
 
24
23
  /**
25
24
  * **IfStartsWith**<TValue, TStartsWith, IF, ELSE, MAYBE>
@@ -32,8 +31,8 @@ export type StartsWith<TValue extends unknown, TStartsWith extends unknown> = TV
32
31
  * type and therefore the type is unknown at design time.
33
32
  */
34
33
  export type IfStartsWith<
35
- TValue extends unknown,
36
- TStartsWith extends unknown,
34
+ TValue extends string,
35
+ TStartsWith extends string,
37
36
  IF extends Narrowable,
38
37
  ELSE extends Narrowable
39
38
  > = StartsWith<TValue, TStartsWith> extends true
@@ -1,4 +1,4 @@
1
- import { Equal } from "../combinators/equivalency";
1
+ import { Equal } from "./equivalency";
2
2
  import { AfterFirst, First } from "../lists";
3
3
  import { Narrowable } from "../Narrowable";
4
4
 
@@ -41,22 +41,24 @@ export type IsFalse<T extends Narrowable> = IsBoolean<T> extends true
41
41
  * to the IF, ELSE, or MAYBE generic types passed in where _maybe_ is when T
42
42
  * is the wide type of `boolean`
43
43
  */
44
- export type IfTrue<T, IF, ELSE, MAYBE> = IsTrue<T> extends true
45
- ? IF
46
- : IsTrue<T> extends false
47
- ? ELSE
48
- : MAYBE;
44
+ export type IfTrue<
45
+ T extends boolean,
46
+ IF extends Narrowable,
47
+ ELSE extends Narrowable,
48
+ MAYBE extends Narrowable
49
+ > = IsTrue<T> extends true ? IF : IsTrue<T> extends false ? ELSE : MAYBE;
49
50
 
50
51
  /**
51
52
  * Type utility which checks for literal `false` value and then switches type
52
53
  * to the IF, ELSE, or MAYBE generic types passed in where _maybe_ is when T
53
54
  * is the wide type of `boolean`
54
55
  */
55
- export type IfFalse<T, IF, ELSE, MAYBE> = IsFalse<T> extends true
56
- ? IF
57
- : IsTrue<T> extends false
58
- ? ELSE
59
- : MAYBE;
56
+ export type IfFalse<
57
+ T extends Narrowable,
58
+ IF extends Narrowable,
59
+ ELSE extends Narrowable,
60
+ MAYBE extends Narrowable
61
+ > = IsFalse<T> extends true ? IF : IsTrue<T> extends false ? ELSE : MAYBE;
60
62
 
61
63
  /**
62
64
  * **IsBooleanLiteral**
@@ -64,13 +66,19 @@ export type IfFalse<T, IF, ELSE, MAYBE> = IsFalse<T> extends true
64
66
  * Type utility which returns true/false if the boolean value is a _boolean literal_ versus
65
67
  * just the wider _boolean_ type.
66
68
  */
67
- export type IsBooleanLiteral<T extends boolean> = boolean extends T ? false : true;
69
+ export type IsBooleanLiteral<T extends Narrowable> = IsTrue<T> extends true
70
+ ? true
71
+ : IsFalse<T> extends true
72
+ ? true
73
+ : false;
68
74
 
69
75
  /**
70
76
  * **IfBooleanLiteral**
71
77
  *
72
78
  * Branch utility which returns `IF` type when `T` is a boolean literal and `ELSE` otherwise
73
79
  */
74
- export type IfBooleanLiteral<T extends boolean, IF, ELSE> = IsBooleanLiteral<T> extends true
75
- ? IF
76
- : ELSE;
80
+ export type IfBooleanLiteral<
81
+ T extends boolean,
82
+ IF extends Narrowable,
83
+ ELSE extends Narrowable
84
+ > = IsBooleanLiteral<T> extends true ? IF : ELSE;
@@ -1,9 +1,7 @@
1
1
  export * from "./array";
2
2
  export * from "./Includes";
3
3
  export * from "./boolean";
4
- export * from "./IsNumericLiteral";
5
4
  export * from "./IsScalar";
6
- export * from "./IsStringLiteral";
7
5
  export * from "./IsLiteral";
8
6
  export * from "./IsUndefined";
9
7
  export * from "./Extends";
@@ -11,3 +9,5 @@ export * from "./TypeDefault";
11
9
  export * from "./object";
12
10
  export * from "./StartsWith";
13
11
  export * from "./EndsWith";
12
+ export * from "./Or";
13
+ export * from "./equivalency";
@@ -1,5 +1,6 @@
1
1
  import { FunctionType } from "../FunctionType";
2
2
  import { Mutable } from "../Mutable";
3
+ import { Narrowable } from "../Narrowable";
3
4
 
4
5
  /**
5
6
  * **IsObject**
@@ -24,4 +25,6 @@ export type IsObject<T> = Mutable<T> extends Record<string, any>
24
25
  * Branch type utility with return `IF` when `T` extends an object type
25
26
  * and `ELSE` otherwise
26
27
  */
27
- export type IfObject<T, IF, ELSE> = IsObject<T> extends true ? IF : ELSE;
28
+ export type IfObject<T, IF extends Narrowable, ELSE extends Narrowable> = IsObject<T> extends true
29
+ ? IF
30
+ : ELSE;
@@ -18,4 +18,4 @@ export type IfString<
18
18
  T extends Narrowable, //
19
19
  IF extends Narrowable,
20
20
  ELSE extends Narrowable
21
- > = IsString<T> extends true ? IF : ELSE;
21
+ > = IsString<T> extends true ? IF : IsString<T> extends false ? ELSE : IF | ELSE;
@@ -37,7 +37,6 @@ export * from "./fluent/index";
37
37
  export * from "./functions/index";
38
38
  export * from "./kv/index";
39
39
  export * from "./lists/index";
40
- export * from "./combinators/index";
41
40
  export * from "./literal-unions/index";
42
41
  export * from "./string-literals/index";
43
42
  export * from "./tuples/index";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * **FilterTuple**
3
+ *
4
+ * Allows a known tuple `T` to be _filtered down_ by eliminating all items
5
+ * in the Tuple that _extend_ type `F`
6
+ * ```ts
7
+ * type T = [1,"foo",3];
8
+ * // [1,3]
9
+ * type T2 = FilterTuple<T, string>;
10
+ * ```
11
+ */
12
+ export type FilterTuple<
13
+ TTuple extends any[] | readonly any[],
14
+ TFilter,
15
+ Result extends any[] = []
16
+ > = TTuple extends [infer A, ...infer R]
17
+ ? [A] extends [TFilter]
18
+ ? FilterTuple<R, TFilter, Result>
19
+ : FilterTuple<R, TFilter, [...Result, A]>
20
+ : Result;
@@ -8,6 +8,7 @@
8
8
  export * from "./AfterFirst";
9
9
  export * from "./First";
10
10
  export * from "./FirstString";
11
+ export * from "./FilterTuple";
11
12
  export * from "./Split";
12
13
  export * from "./UniqueForProp";
13
14
 
@@ -0,0 +1,87 @@
1
+ import { Equal, Expect } from "@type-challenges/utils";
2
+ import { pathJoin } from "src/runtime/literals/pathJoin";
3
+ import { PathJoin } from "src/types/alphabetic/PathJoin";
4
+ import { describe, expect, it } from "vitest";
5
+
6
+ // Note: type tests fail visible inspection but pass from Vitest
7
+ // standpoint so always be sure to run `tsc --noEmit` over your test files to
8
+ // gain validation that no new type vulnerabilities have cropped up.
9
+
10
+ describe("PathJoin<T,U>", () => {
11
+ it("only literals / happy path", () => {
12
+ type T1 = PathJoin<"foo", "bar">;
13
+ type T2 = PathJoin<"foo/", "bar">;
14
+ type T3 = PathJoin<"foo", "/bar">;
15
+ type T4 = PathJoin<"foo/", "/bar">;
16
+
17
+ type cases = [
18
+ // neither have divider
19
+ Expect<Equal<T1, "foo/bar">>,
20
+ // one has, one does not
21
+ Expect<Equal<T2, "foo/bar">>,
22
+ Expect<Equal<T3, "foo/bar">>,
23
+ // both have
24
+ Expect<Equal<T4, "foo/bar">>
25
+ ];
26
+ const cases: cases = [true, true, true, true];
27
+ });
28
+
29
+ it("wide types mixed in", () => {
30
+ type T1 = PathJoin<"foo", string>;
31
+ type T2 = PathJoin<"foo/", string>;
32
+ type T3 = PathJoin<string, "/bar">;
33
+ type T4 = PathJoin<string, "bar">;
34
+
35
+ type cases = [
36
+ Expect<Equal<T1, `foo/${string}`>>,
37
+ Expect<Equal<T2, `foo/${string}`>>,
38
+ Expect<Equal<T3, `${string}/bar`>>,
39
+ Expect<Equal<T4, `${string}bar`>>
40
+ ];
41
+ const cases: cases = [true, true, true, true];
42
+ });
43
+ });
44
+
45
+ describe("pathJoin() runtime util", () => {
46
+ it("happy path", () => {
47
+ const t1 = pathJoin("foo", "bar" as string);
48
+ const t2 = pathJoin("foo/", "bar" as string);
49
+ const t3 = pathJoin("foo" as string, "/bar");
50
+ const t4 = pathJoin("foo/" as string, "/bar");
51
+ const t5 = pathJoin("foo/" as string, "bar");
52
+
53
+ // runtime tests
54
+ [t1, t2, t3, t4].forEach((test) => expect(test).toBe("foo/bar"));
55
+
56
+ // type tests
57
+ type cases = [
58
+ Expect<Equal<typeof t1, `foo/${string}`>>,
59
+ Expect<Equal<typeof t2, `foo/${string}`>>,
60
+ Expect<Equal<typeof t3, `${string}/bar`>>,
61
+ Expect<Equal<typeof t4, `${string}/bar`>>,
62
+ Expect<Equal<typeof t5, `${string}bar`>>
63
+ ];
64
+ const cases: cases = [true, true, true, true, true];
65
+ });
66
+
67
+ it("with wide types", () => {
68
+ const t1 = pathJoin("foo", "bar");
69
+ const t2 = pathJoin("foo/", "bar");
70
+ const t3 = pathJoin("foo", "/bar");
71
+ const t4 = pathJoin("foo/", "/bar");
72
+ // runtime tests
73
+ [t1, t2, t3, t4].forEach((test) => expect(test, "runtime failure").toBe("foo/bar"));
74
+
75
+ // type tests
76
+ type cases = [
77
+ // neither have divider
78
+ Expect<Equal<typeof t1, "foo/bar">>,
79
+ // one has, one does not
80
+ Expect<Equal<typeof t2, "foo/bar">>,
81
+ Expect<Equal<typeof t3, "foo/bar">>,
82
+ // both have
83
+ Expect<Equal<typeof t4, "foo/bar">>
84
+ ];
85
+ const cases: cases = [true, true, true, true];
86
+ });
87
+ });
@@ -11,7 +11,7 @@ import {
11
11
  ifUndefined,
12
12
  isTrue,
13
13
  } from "src/runtime/type-checks";
14
- import { EndsWith, Extends, Or, StartsWith } from "src/types";
14
+ import { EndsWith, Extends, LowerAlpha, Or, StartsWith } from "src/types";
15
15
  import { ifStartsWith, startsWith } from "src/runtime/type-checks/startsWith";
16
16
  import { box } from "src/runtime/literals";
17
17
  import { or } from "src/runtime";
@@ -236,14 +236,21 @@ describe("runtime if/is", () => {
236
236
  type T4 = StartsWith<string, "foo">;
237
237
  type T5 = StartsWith<string, string>;
238
238
 
239
+ type T6 = StartsWith<"alpha", LowerAlpha>;
240
+ type T7 = StartsWith<"Alpha", LowerAlpha>;
241
+
239
242
  type cases = [
240
243
  Expect<Equal<T1, true>>, //
241
244
  Expect<Equal<T2, false>>,
245
+ // if either the "start with" or "val" props are wide then we can't resolve at design time
242
246
  Expect<Equal<T3, boolean>>,
243
247
  Expect<Equal<T4, boolean>>,
244
- Expect<Equal<T5, boolean>>
248
+ Expect<Equal<T5, boolean>>,
249
+ // LowerAlpha is a string literal
250
+ Expect<Equal<T6, true>>,
251
+ Expect<Equal<T7, false>>
245
252
  ];
246
- const cases: cases = [true, true, true, true, true];
253
+ const cases: cases = [true, true, true, true, true, true, true];
247
254
  });
248
255
 
249
256
  it("EndsWith<T,U>", () => {
@@ -288,7 +295,7 @@ describe("runtime if/is", () => {
288
295
  // condition
289
296
  "foo",
290
297
  // inline
291
- (i) => `welcome ${i}` as const,
298
+ (i) => `welcome ${i}`,
292
299
  // external
293
300
  noSir
294
301
  );
@@ -1,16 +0,0 @@
1
- /**
2
- * **IsNumericLiteral**
3
- *
4
- * Type utility which returns true/false if the numeric value a _numeric literal_ versus
5
- * just the _number_ type.
6
- */
7
- export type IsNumericLiteral<T extends number> = number extends T ? false : true;
8
-
9
- /**
10
- * **IfNumericLiteral**
11
- *
12
- * Branch utility which returns `IF` type when `T` is a numeric literal and `ELSE` otherwise
13
- */
14
- export type IfNumericLiteral<T extends number, IF, ELSE> = IsNumericLiteral<T> extends true
15
- ? IF
16
- : ELSE;
@@ -1,14 +0,0 @@
1
- /**
2
- * **IsStringLiteral**
3
- *
4
- * Type utility which returns true/false if the string a _string literal_ versus
5
- * just the _string_ type.
6
- */
7
- export type IsStringLiteral<T> = [T] extends [string] ? (string extends T ? false : true) : false;
8
-
9
- /**
10
- * **IfStringLiteral**
11
- *
12
- * Branch utility which returns `IF` type when `T` is a string literal and `ELSE` otherwise
13
- */
14
- export type IfStringLiteral<T, IF, ELSE> = [IsStringLiteral<T>] extends [true] ? IF : ELSE;
@@ -1,2 +0,0 @@
1
- export * from "./equivalency";
2
- export * from "./Or";