inferred-types 0.34.2 → 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.
- package/dist/index.d.ts +33 -1
- package/dist/index.js +16 -0
- package/dist/index.mjs +14 -0
- package/package.json +8 -8
- package/src/runtime/literals/index.ts +2 -0
- package/src/runtime/literals/pathJoin.ts +17 -0
- package/src/runtime/literals/stripEnding.ts +9 -0
- package/src/runtime/literals/stripStarting.ts +15 -0
- package/src/types/alphabetic/PathJoin.ts +40 -0
- package/src/types/alphabetic/StripEnding.ts +23 -0
- package/src/types/alphabetic/StripStarting.ts +23 -0
- package/src/types/alphabetic/index.ts +2 -0
- package/tests/literals/PathJoin.test.ts +87 -0
package/dist/index.d.ts
CHANGED
|
@@ -1257,6 +1257,34 @@ type SpaceToDash<T extends string> = T extends `${infer Begin}${" "}${infer Rest
|
|
|
1257
1257
|
* ``` */
|
|
1258
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>>>>>;
|
|
1259
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
|
+
|
|
1260
1288
|
type NetworkProtocol = "http" | "https" | "file" | "ws" | "wss";
|
|
1261
1289
|
/**
|
|
1262
1290
|
* A literal variant of _string_ which is meant to represent a domain name
|
|
@@ -2235,6 +2263,10 @@ declare function idTypeGuard<T extends {
|
|
|
2235
2263
|
*/
|
|
2236
2264
|
declare function literal<N extends Narrowable, T extends Record<keyof T, N>>(obj: T): T;
|
|
2237
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
|
+
|
|
2238
2270
|
/**
|
|
2239
2271
|
* **Suggest**
|
|
2240
2272
|
*
|
|
@@ -2541,4 +2573,4 @@ interface IFluentConfigurator<C> {
|
|
|
2541
2573
|
*/
|
|
2542
2574
|
declare function FluentConfigurator<I>(initial?: I): IFluentConfigurator<{}>;
|
|
2543
2575
|
|
|
2544
|
-
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, 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.
|
|
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.
|
|
36
|
-
"@typescript-eslint/eslint-plugin": "^5.45.
|
|
37
|
-
"@typescript-eslint/parser": "^5.45.
|
|
38
|
-
"@vitest/ui": "^0.25.
|
|
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.
|
|
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.
|
|
57
|
-
"vitest": "^0.25.
|
|
56
|
+
"vite": "^3.2.5",
|
|
57
|
+
"vitest": "^0.25.4"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"brilliant-errors": "^0.6.1"
|
|
@@ -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
|
+
}
|
|
@@ -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
|
|
|
@@ -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
|
+
});
|