mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 02:44:55 +00:00
chore: add more documentation for strapi/types.Utils
This commit is contained in:
parent
f2213fa51c
commit
14a91aaea5
@ -1,20 +1,84 @@
|
||||
import type { Extends, Not, Constants } from './';
|
||||
import type { Extends, Not } from './';
|
||||
|
||||
/**
|
||||
* Extract the array values into a union type
|
||||
* The `Values` type extracts the type of the values stored in arrays or tuples.
|
||||
*
|
||||
* @returns A union of every value contained in the array/tuple
|
||||
*
|
||||
* @template TCollection - The array-like structure from which the values' type will be extracted.
|
||||
*
|
||||
* @example
|
||||
* Let's suppose we have an array of numbers, and we would like to extract the type its values
|
||||
*
|
||||
* ```typescript
|
||||
* type MyTuple = [1, 2, 3, 4];
|
||||
*
|
||||
* type TupleValues = Values<MyTuple>; // TupleValues: 1 | 2 | 3 | 4
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* Now, let's suppose we have a regular TypeScript array, and we would like to extract its value
|
||||
*
|
||||
* ```typescript
|
||||
* type MyArray = (string | number)[];
|
||||
*
|
||||
* type ArrayValues = Values<MyArray>; // ArrayValues: string | number
|
||||
* ```
|
||||
*/
|
||||
export type Values<TCollection extends Array<unknown>> = TCollection extends Array<infer TValues>
|
||||
? TValues
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Checks if the size of the given collection equals 0
|
||||
* Checks if a given array ({@link TCollection}) is empty.
|
||||
*
|
||||
* @template TCollection - The array to be checked. It should extend 'Array<unknown>'.
|
||||
*
|
||||
* @example
|
||||
* Validate an array that is empty:
|
||||
* ```typescript
|
||||
* type EmptyArray = [];
|
||||
*
|
||||
* type IsEmptyCheck = IsEmpty<EmptyArray>;
|
||||
* // Result: Constants.True
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* Validate an array that is not empty:
|
||||
* ```typescript
|
||||
* type NonEmptyArray = [1, 2, 3];
|
||||
*
|
||||
* type IsEmptyCheck = IsEmpty<NonEmptyArray>;
|
||||
* // Result: Constants.False
|
||||
* ```
|
||||
*/
|
||||
export type IsEmpty<TCollection extends Array<unknown>> = Extends<TCollection['length'], 0>;
|
||||
|
||||
/**
|
||||
* Checks if the size of the given collection is not 0
|
||||
* Checks if a given array ({@link TCollection}) is not empty.
|
||||
*
|
||||
* Returns a {@link Constants.BooleanValue} expression
|
||||
* @template TCollection - The collection (array) that needs to be checked if it's not empty. It must extend 'Array<unknown>'.
|
||||
*
|
||||
* @see {@link Not}
|
||||
* @see {@link IsEmpty}
|
||||
*
|
||||
* @example
|
||||
* Checking non-empty array:
|
||||
* ```typescript
|
||||
* type NonEmptyArray = [1, 2, 3];
|
||||
*
|
||||
* // This type checks will result to True because
|
||||
* // 'NonEmptyArray' is indeed not empty.
|
||||
* type IsNotEmptyCheck = IsNotEmpty<NonEmptyArray>;
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* Checking empty array:
|
||||
* ```typescript
|
||||
* type EmptyArray = [];
|
||||
*
|
||||
* // This type checks will result to False because 'EmptyArray' is empty.
|
||||
* type IsNotEmptyCheck = IsNotEmpty<EmptyArray>;
|
||||
* ```
|
||||
*/
|
||||
export type IsNotEmpty<TCollection extends Array<unknown>> = Not<IsEmpty<TCollection>>;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type * as Internal from '../internal';
|
||||
import type * as UID from '../uid';
|
||||
|
||||
import type { Or, NotStrictEqual } from '../utils';
|
||||
import type { NotStrictEqual, Or } from '../utils';
|
||||
|
||||
export type True = true;
|
||||
|
||||
@ -9,14 +9,71 @@ export type False = false;
|
||||
|
||||
export type BooleanValue = True | False;
|
||||
|
||||
/**
|
||||
* Determine if there's been an extension or change in either Component Registry or Content-Type Registry.
|
||||
*
|
||||
* It applies the compound boolean OR operation on the results derived from {@link IsComponentRegistryExtended} and {@link IsContentTypeRegistryExtended}.
|
||||
*
|
||||
* If either or both results are true (indicating a change or extension in the corresponding registry), it returns {@link True}; otherwise, it returns {@link False}.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // A change or extension in both the Component Registry and the Content-Type Registry
|
||||
* type Example1 = AreSchemaRegistriesExtended; // Result: Constants.True
|
||||
*
|
||||
* // A change or extension only in the Component Registry
|
||||
* type Example2 = IsComponentRegistryExtended; // Result: Constants.True
|
||||
*
|
||||
* // A change or extension only in the Content-Type Registry
|
||||
* type Example3 = IsContentTypeRegistryExtended; // Result: Constants.True
|
||||
|
||||
* // No change or extension in either registries
|
||||
* type Example4 = AreSchemaRegistriesExtended; // Result: Constants.False
|
||||
* ```
|
||||
*
|
||||
* @see IsComponentRegistryExtended
|
||||
* @see IsContentTypeRegistryExtended
|
||||
*/
|
||||
export type AreSchemaRegistriesExtended = Or<
|
||||
IsComponentRegistryExtended,
|
||||
IsContentTypeRegistryExtended
|
||||
>;
|
||||
|
||||
/**
|
||||
* Evaluate if the internal UIDs ({@link Internal.UID.ContentType}) and public ones ({@link UID.ContentType}) are not identical.
|
||||
*
|
||||
* If these two types are not the same, it indicates an extension or change has occurred in the public Content-Type Registry.
|
||||
*
|
||||
* The type leverages {@link NotStrictEqual} to perform this comparison accurately. The result is a type-level
|
||||
* boolean that denotes whether there is a deviation between the two Content-Type representations.
|
||||
*
|
||||
* @returns Either [Constants.True](@link Constants.True) if the Content-Type Registry has been extended, else [Constants.False](@link Constants.False).
|
||||
*
|
||||
* @remark
|
||||
* This type is particularly useful when there is a need to verify whether there have been extensions or changes to the Content-Type Registry
|
||||
* after initially creating it.
|
||||
*
|
||||
* It allows developers to perform this check at the type level and decide what type should be resolved depending on the context
|
||||
*/
|
||||
export type IsContentTypeRegistryExtended = NotStrictEqual<
|
||||
Internal.UID.ContentType,
|
||||
UID.ContentType
|
||||
>;
|
||||
|
||||
/**
|
||||
* Evaluate if the internal UIDs ({@link Internal.UID.Component}) and public ones ({@link UID.Component}) are not identical.
|
||||
*
|
||||
* If these two types are not the same, it indicates an extension or change has occurred in the public Component Registry.
|
||||
*
|
||||
* The type leverages {@link NotStrictEqual} to perform this comparison accurately. The result is a type-level
|
||||
* boolean that denotes whether there is a deviation between the two Content-Type representations.
|
||||
*
|
||||
* @return - Either {@link True} if the Component Registry has been extended, else {@link False}.
|
||||
*
|
||||
* @remark
|
||||
* This type is particularly useful when there is a need to verify whether there have been extensions or changes to the Component Registry
|
||||
* after initially creating it.
|
||||
*
|
||||
* It allows developers to perform this check at the type level and decide what type should be resolved depending on the context
|
||||
*/
|
||||
export type IsComponentRegistryExtended = NotStrictEqual<Internal.UID.Component, UID.Component>;
|
||||
|
||||
@ -1,37 +1,271 @@
|
||||
import type { Array, Constants, Guard, Simplify } from './';
|
||||
import type { Array, Constants } from './';
|
||||
|
||||
/**
|
||||
* The `IsNever` type checks if a given type {@link TValue} strictly equals to `never`.
|
||||
*
|
||||
* @template TValue - The type variable to be checked against `never`.
|
||||
*
|
||||
* @example
|
||||
* type A = IsNever<number>; // This will resolve to 'false' because number is not `never`
|
||||
* type B = IsNever<Cast<'foo', number>>; // This will resolve to 'true' because Cast<'foo', number> strictly equals to never.
|
||||
*
|
||||
* @see {@link StrictEqual} - The Type used internally to make the comparison.
|
||||
* @remark
|
||||
* Please make sure to understand the difference between `never` and other types in TypeScript before using `IsNever` for any conditional checks
|
||||
*/
|
||||
export type IsNever<TValue> = StrictEqual<TValue, never>;
|
||||
|
||||
/**
|
||||
* The `IsNotNever` type checks if a given type {@link TValue} does not strictly equals to `never`.
|
||||
*
|
||||
* It is useful in conditional types to verify if the variable of type {@link TValue} is something other than `never`.
|
||||
* It complements the {@link IsNever} type by negating the result using the {@link Not} utility type.
|
||||
*
|
||||
* @template TValue - The type variable to be checked for inequality against `never`.
|
||||
*
|
||||
* @example
|
||||
* type IsNotNeverNumber = IsNotNever<number>; // Evaluates to 'true' because number is not 'never'.
|
||||
* type IsNotNeverNever = IsNotNever<never>; // Evaluates to 'false' because `never` equals to 'never'.
|
||||
*
|
||||
* @see {@link IsNever} - The type used internally to check if {@link TValue} is `never`.
|
||||
*/
|
||||
export type IsNotNever<TValue> = Not<IsNever<TValue>>;
|
||||
|
||||
/**
|
||||
* The `IsTrue` type evaluates if the given {@link TValue} strictly equals {@link Constants.True}.
|
||||
*
|
||||
* @template TValue - The type to evaluate.
|
||||
*
|
||||
* @example
|
||||
* type A = IsTrue<true>; // This will resolve to Constants.True
|
||||
* type B = IsTrue<false>; // This will resolve to Constants.False
|
||||
*/
|
||||
export type IsTrue<TValue> = [TValue] extends [Constants.True] ? Constants.True : Constants.False;
|
||||
|
||||
/**
|
||||
* The `IsNotTrue` type evaluates if the given {@link TValue} is not strictly equal to {@link Constants.True}.
|
||||
*
|
||||
* It basically negates the output of {@link IsTrue}.
|
||||
*
|
||||
* @template TValue - The type to evaluate.
|
||||
*
|
||||
* @example
|
||||
* type A = IsNotTrue<true>; // This will resolve to Constants.False
|
||||
* type B = IsNotTrue<false>; // This will resolve to Constants.True
|
||||
*
|
||||
*/
|
||||
export type IsNotTrue<TValue> = Not<IsTrue<TValue>>;
|
||||
|
||||
/**
|
||||
* The `IsFalse` type evaluates if the given {@link TValue} strictly equals {@link Constants.False}.
|
||||
*
|
||||
* @template TValue - The type to evaluate.
|
||||
*
|
||||
* @example
|
||||
* type A = IsFalse<true>; // This will resolve to Constants.False
|
||||
* type B = IsFalse<false>; // This will resolve to Constants.True
|
||||
*/
|
||||
export type IsFalse<TValue> = [TValue] extends [Constants.False] ? Constants.True : Constants.False;
|
||||
|
||||
/**
|
||||
* The `IsNotFalse` type evaluates if the value provided does not strictly equal {@link Constants.False}.
|
||||
*
|
||||
* It basically negates the output of {@link IsFalse}.
|
||||
*
|
||||
* @template TValue - The type to be evaluated.
|
||||
*
|
||||
* @example
|
||||
* type A = IsNotFalse<false>; // This will resolve to Constants.False
|
||||
* type B = IsNotFalse<true>; // This will resolve to Constants.True
|
||||
*/
|
||||
export type IsNotFalse<TValue> = Not<IsFalse<TValue>>;
|
||||
|
||||
/**
|
||||
* The `StrictEqual` type evaluates if two types, {@link TValue} and {@link TMatch}, are strictly the same.
|
||||
*
|
||||
* In other words, it checks if {@link TValue} extends {@link TMatch} and if {@link TMatch} extends {@link TValue} at the same time,
|
||||
* hence ensuring complete type match.
|
||||
*
|
||||
* @template TValue - The first type to be compared.
|
||||
* @template TMatch - The second type to be compared.
|
||||
*
|
||||
* @returns Either {@link Constants.True} or {@link Constants.False}.
|
||||
*
|
||||
* @example
|
||||
* // With a regular extends
|
||||
* type A = "string" extends string ? true : false; // Result: true
|
||||
*
|
||||
* // With `StrictEqual`
|
||||
* type B = StrictEqual<"string", string>; // Result: false
|
||||
*/
|
||||
export type StrictEqual<TValue, TMatch> = And<Extends<TValue, TMatch>, Extends<TMatch, TValue>>;
|
||||
|
||||
/**
|
||||
* The `NotStrictEqual` type is a utility type that checks if two types, {@link TValue} and {@link TMatch}, are different using strict equality comparison.
|
||||
*
|
||||
*
|
||||
* @template TValue - The first type to be compared.
|
||||
* @template TMatch - The second type to be compared against the first one.
|
||||
*
|
||||
* @returns Either {@link Constants.True} or {@link Constants.False}
|
||||
*
|
||||
* @see {@link StrictEqual}
|
||||
*
|
||||
* @example
|
||||
* // Comparing basic types
|
||||
* type BasicTypeCheck = NotStrictEqual<number, string>; // Result: Constants.True (because `number` and `string` types are not the same)
|
||||
*
|
||||
* // Comparing complex types
|
||||
* type MyType = { a: number, b: string };
|
||||
* type OtherType = { a: number, c: boolean };
|
||||
* type ComplexTypeCheck = NotStrictEqual<MyType, OtherType>; // Result: Constants.True (because `MyType` and `OtherType` do not have the same structure)
|
||||
*
|
||||
*/
|
||||
export type NotStrictEqual<TValue, TMatch> = Not<StrictEqual<TValue, TMatch>>;
|
||||
|
||||
/**
|
||||
* The `Extends` type evaluates if a type, identified by {@link TLeft}, extends another one, identified by {@link TRight}.
|
||||
*
|
||||
* @template TLeft - The type to be tested if it extends {@link TRight}.
|
||||
* @template TRight - The base type used for comparison.
|
||||
*
|
||||
* @note To understand more about conditional types and the `extends` keyword in TypeScript see {@link https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types}
|
||||
*
|
||||
* @remark
|
||||
* The check `[TLeft] extends [TRight]` is wrapped in a tuple because TypeScript's `extends` behaves differently with unions in the context of distributivity.
|
||||
*
|
||||
* Wrapping in a tuple deactivates this distributive behavior and makes the check behave as expected in all cases.
|
||||
*
|
||||
* @example
|
||||
* The type `"hello"` is a subtype of `string` so it extends `string`
|
||||
* ```typescript
|
||||
* type isString = Extends<"hello", string>;
|
||||
* // output: Constants.True
|
||||
*```
|
||||
*
|
||||
* The type `string` does not extend `"hello"`.
|
||||
* ```typescript
|
||||
* type notSpecificString = Extends<string, "hello">;
|
||||
* // output: Constants.False
|
||||
* ```
|
||||
*/
|
||||
export type Extends<TLeft, TRight> = [TLeft] extends [TRight] ? Constants.True : Constants.False;
|
||||
|
||||
/**
|
||||
* The `DoesNotExtends` type checks if {@link TLeft} does not extend {@link TRight}.
|
||||
*
|
||||
* @template TLeft - The type to be tested if it does not extend {@link TRight}.
|
||||
* @template TRight - The base type used for comparing if it is not extended by {@link TLeft}.
|
||||
*
|
||||
* @see {@link Extends}
|
||||
*/
|
||||
export type DoesNotExtends<TLeft, TRight> = Not<Extends<TLeft, TRight>>;
|
||||
|
||||
/**
|
||||
* The `Not` type defines a type-level boolean negation operation.
|
||||
*
|
||||
* More concretely, if {@link TExpression} strictly equates to {@link Constants.True} then the result of `Not<TExpression>` would be {@link Constants.False}, and vice versa.
|
||||
*
|
||||
* @template TExpression - The type level boolean expression to be negated. It should extend {@link Constants.BooleanValue}.
|
||||
*
|
||||
* @see {@link Constants.BooleanValue}
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Using expression that equates to `true`
|
||||
* type A = Not<Constants.True>; // Results in Constants.False
|
||||
*
|
||||
* // Using `true` wrapped inside another type
|
||||
* type B = Not<IsTrue<true>>; // Results in Constants.False
|
||||
*
|
||||
* // Using expression that equates to `false`
|
||||
* type C = Not<Constants.False>; // Results in Constants.True
|
||||
*
|
||||
* // Using `false` wrapped inside another type
|
||||
* type D = Not<IsFalse<false>>; // Results in Constants.True
|
||||
* ```
|
||||
*/
|
||||
export type Not<TExpression extends Constants.BooleanValue> = If<
|
||||
TExpression,
|
||||
Constants.False,
|
||||
Constants.True
|
||||
>;
|
||||
|
||||
/**
|
||||
* The `If` type is a conditional type that accepts a type level boolean expression (`true` or `false` represented as {@link Constants.BooleanValue}),
|
||||
* and two result types, one if the expression is {@link Constants.True} and the other if it's {@link Constants.False}.
|
||||
*
|
||||
* It's an implementation of the traditional 'if/then/else' logic, but at the type level.
|
||||
*
|
||||
* @template TExpression - The type level boolean expression to evaluate. It should extend {@link Constants.BooleanValue}.
|
||||
* @template TOnTrue - The type returned if {@link TExpression} resolves to {@link Constants.True}.
|
||||
* @template TOnFalse - The type returned if {@link TExpression} resolves to {@link Constants.False}. It defaults to `never`.
|
||||
*
|
||||
* @example
|
||||
* Here's an example using `If` with {@link TExpression} that's resolved to {@link Constants.False}.
|
||||
*
|
||||
* As a result, the type applied will be 'FalseCase'.
|
||||
* ```typescript
|
||||
* type FalseCase = 'This is False';
|
||||
* type TrueCase = 'This is True';
|
||||
*
|
||||
* type Result = If<Constants.False, TrueCase, FalseCase>; // Result: 'This is False'
|
||||
* ```
|
||||
*
|
||||
* Conversely, if we replace {@link TExpression} with {@link Constants.True}, the applicable type will be 'TrueCase'.
|
||||
* ```typescript
|
||||
* type ExampleTrue = If<Constants.True, TrueCase, FalseCase>; // Result: 'This is True case'
|
||||
* ```
|
||||
*
|
||||
* If the third type argument is omitted, and the expression is false, the `If` type will resolve to `never`.
|
||||
* ```typescript
|
||||
* type ExampleNever = If<Constants.False, TrueCase>; // Result: never
|
||||
* ```
|
||||
*/
|
||||
export type If<TExpression extends Constants.BooleanValue, TOnTrue, TOnFalse = never> = [
|
||||
TExpression
|
||||
] extends [Constants.True]
|
||||
? TOnTrue
|
||||
: TOnFalse;
|
||||
|
||||
/**
|
||||
* The `MatchFirst` type is a type-level logic that matches the first truthy `Test` in a given array of {@link Test} types
|
||||
* and resolves to corresponding type value ('TValue') of that {@link Test}.
|
||||
*
|
||||
* If no truthy {@link Test} is found, it resolves to a default type {@link TDefault}.
|
||||
*
|
||||
* @note This type is particularly useful for checking multiple conditions and matching the type to
|
||||
* whichever condition proves true first, similar to a switch-case or a series of if-else statements in traditional programming.
|
||||
*
|
||||
* @template TTests - An array of {@link Test} types that the type checker will iterate over to find the first truthy test.
|
||||
* @template TDefault - The default value that will be used if none of the {@link Test} types in {@link TTests} prove true. Defaults to `never`.
|
||||
*
|
||||
* @see {@link Test}
|
||||
* @see {@link MatchAllIntersect}
|
||||
*
|
||||
* @example
|
||||
* Here's an example showing how `MatchFirst` can be used with series of {@link Test} types.
|
||||
*
|
||||
* We have declared a Test array containing two Test types.
|
||||
* - The first Test type checks if 'T' is a string.
|
||||
*
|
||||
* If true, it will return 'string type', else it moves to the next Test type.
|
||||
* - The next Test type checks if 'T' is a number.
|
||||
*
|
||||
* If true, it will return 'number type'.
|
||||
* - The third argument is the default type which would be returned if all the conditions fail. In our case its 'unknown type'.
|
||||
*
|
||||
* ```typescript
|
||||
* type T = string; // you can replace 'T' with 'number' or 'boolean' to test.
|
||||
*
|
||||
* type IsString = Test<Extends<T, string>, 'string type'>;
|
||||
* type IsNumber = Test<Extends<T, number>, 'number type'>;
|
||||
* type Tests = [IsString, IsNumber];
|
||||
*
|
||||
* type Result = MatchFirst<Tests, 'unknown type'>; // Result would be 'string type' as 'T' is string.
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export type MatchFirst<TTests extends Test[], TDefault = never> = TTests extends [
|
||||
infer THead extends Test,
|
||||
...infer TTail extends Test[]
|
||||
@ -41,6 +275,45 @@ export type MatchFirst<TTests extends Test[], TDefault = never> = TTests extends
|
||||
: never
|
||||
: never;
|
||||
|
||||
/**
|
||||
* The `MatchAllIntersect` type enables the creation of an intersection type from a sequence of conditional types.
|
||||
*
|
||||
* It is useful in scenarios where the properties of an object are to be picked conditionally, based on evaluated boolean expressions.
|
||||
*
|
||||
* @template TTests - A tuple type where each member extends {@link Test}.
|
||||
*
|
||||
* It's this sequence of tests that determine the properties to be picked.
|
||||
*
|
||||
* @template TDefault - This type is used whenever a member of `TTests` doesn't match the expected type or when the
|
||||
* tuple is empty, meaning that no conditions were provided.
|
||||
*
|
||||
* This defaults to `unknown`.
|
||||
*
|
||||
* @see {@link Test}
|
||||
* @see {@link MatchFirst}
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type Test1 = Test<Constants.True, { sort?: string }>;
|
||||
* type Test2 = Test<Constants.False, { fields?: number[] }>;
|
||||
* type Test3 = Test<Constants.True, { populate?: string[] }>;
|
||||
*
|
||||
* type Result = MatchAllIntersect<[Test1, Test2, Test3]>;
|
||||
* // The Result will be { sort?: string } & { populate?: string[] }
|
||||
* ```
|
||||
*
|
||||
* In the example above, only Test1 and Test3 resolves to true case and thus the result excludes the type `{ fields?: number[] }`.
|
||||
*
|
||||
* There is also a default case `{}` that would be returned if *all* the tests in `TTests` were false.
|
||||
*
|
||||
* This can be customized by using the second type parameter {@link TDefault}.
|
||||
* ```typescript
|
||||
* type Test3 = Test<Constants.False, { sort?: string }>;
|
||||
* type Test4 = Test<Constants.False, { fields?: number[] }>;
|
||||
*
|
||||
* type ResultDefault = MatchAllIntersect<[Test3, Test4], {}>; // The Result will be {}
|
||||
* ```
|
||||
*/
|
||||
export type MatchAllIntersect<TTests extends Test[], TDefault = unknown> = TTests extends [
|
||||
infer THead extends Test,
|
||||
...infer TTail extends Test[]
|
||||
@ -53,11 +326,65 @@ export type MatchAllIntersect<TTests extends Test[], TDefault = unknown> = TTest
|
||||
: TDefault
|
||||
: TDefault;
|
||||
|
||||
/**
|
||||
* The `Test` type pairs a boolean expression and a corresponding value.
|
||||
*
|
||||
* The elements of the type pair are:
|
||||
* 1. A boolean value ({@link TExpression}), which acts as the conditional expression.
|
||||
* 2. A corresponding value ({@link TValue}), which is usually returned/read when the conditional expression is `true`.
|
||||
*
|
||||
* @template TExpression - A boolean value that will be used as the conditional expression. It extends from {@link Constants.BooleanValue}.
|
||||
* @template TValue - The corresponding value that will be returned when the `TExpression` is `true`.
|
||||
*
|
||||
* @see {@link Constants.BooleanValue}
|
||||
* @see {@link MatchFirst}
|
||||
* @see {@link MatchAllIntersect}
|
||||
*
|
||||
* @example
|
||||
* Suppose we're writing a type level function that behaves differently based on whether the generic type parameter extends a string or a number.
|
||||
*
|
||||
* We can represent these two conditions using the `Test` type, like this:
|
||||
*
|
||||
* ```typescript
|
||||
* type T = number; // replace this with different types to see the outcome
|
||||
*
|
||||
* // Defining two Test conditions
|
||||
* type IsString = Test<Extends<T, string>, 'Input is a string'>;
|
||||
* type IsNumber = Test<Extends<T, number>, 'Input is a number'>;
|
||||
*
|
||||
* type DetectedType = MatchFirst<[IsString, IsNumber], 'unknown type'>; // The Result will be 'Input is a number'
|
||||
* ```
|
||||
*/
|
||||
export type Test<
|
||||
TExpression extends Constants.BooleanValue = Constants.BooleanValue,
|
||||
TValue = unknown
|
||||
> = [TExpression, TValue];
|
||||
|
||||
/**
|
||||
* The `Some` type is used for performing a boolean OR operation at the type level over all elements of {@link TExpressions}.
|
||||
*
|
||||
* The OR operation is applied between every two adjacent types in the array from left to right until a resulting type is derived.
|
||||
*
|
||||
* It's conceptually similar to the `Array.prototype.some()` method, but at the type level rather than the value level.
|
||||
*
|
||||
* If the array is empty, it returns {@link Constants.False}.
|
||||
*
|
||||
* @template TExpressions - An array of types extending {@link Constants.BooleanValue}. Use this to specify the types to apply the OR operation on.
|
||||
*
|
||||
* @see {@link Every}
|
||||
* @see {@link Constants.BooleanValue}
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type Example1 = Some<[Constants.False, Constants.False, Constants.False]>; // Result: Constants.False
|
||||
* type Example2 = Some<[Constants.False, Constants.True, Constants.False]>; // Result: Constants.True
|
||||
* type Example3 = Some<[Constants.True, Constants.True, Constants.True]>; // Result: Constants.True
|
||||
* type Example4 = Some<[Constants.False]>; // Result: Constants.False
|
||||
* type Example5 = Some<[Constants.True]>; // Result: Constants.True
|
||||
* type Example6 = Some<[]>; // Result: Constants.False
|
||||
* ```
|
||||
*/
|
||||
export type Some<TExpressions extends Constants.BooleanValue[]> = TExpressions extends [
|
||||
infer THead extends Constants.BooleanValue,
|
||||
...infer TTail extends Constants.BooleanValue[]
|
||||
@ -65,6 +392,30 @@ export type Some<TExpressions extends Constants.BooleanValue[]> = TExpressions e
|
||||
? If<Array.IsNotEmpty<TTail>, Or<THead, Some<TTail>>, Or<THead, false>>
|
||||
: never;
|
||||
|
||||
/**
|
||||
* The `Every` type is used to perform a logical AND operation on a sequence of type-level boolean values represented as {@link Constants.BooleanValue}.
|
||||
*
|
||||
* The AND operation is applied between every two adjacent types in the array from left to right until a resulting type is derived.
|
||||
*
|
||||
* It's conceptually similar to the `Array.prototype.every()` method, but at the type level rather than the value level.
|
||||
*
|
||||
* If the array is empty, it returns {@link Constants.True}.
|
||||
*
|
||||
* @template TExpressions - An array of types extending {@link Constants.BooleanValue}. Use this to specify the types to apply the AND operation on.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type Example1 = Every<[Constants.False, Constants.False, Constants.False]>; // Result: Constants.False
|
||||
* type Example2 = Every<[Constants.False, Constants.True, Constants.False]>; // Result: Constants.False
|
||||
* type Example3 = Every<[Constants.True, Constants.True, Constants.True]>; // Result: Constants.True
|
||||
* type Example4 = Every<[Constants.False]>; // Result: Constants.False
|
||||
* type Example5 = Every<[Constants.True]>; // Result: Constants.True
|
||||
* type Example6 = Every<[]>; // Result: Constants.True
|
||||
* ```
|
||||
*
|
||||
* @see {@link Some}
|
||||
* @see {@link Constants.BooleanValue}
|
||||
*/
|
||||
export type Every<TExpressions extends Constants.BooleanValue[]> = TExpressions extends [
|
||||
infer THead extends Constants.BooleanValue,
|
||||
...infer TTail extends Constants.BooleanValue[]
|
||||
@ -72,15 +423,114 @@ export type Every<TExpressions extends Constants.BooleanValue[]> = TExpressions
|
||||
? If<Array.IsNotEmpty<TTail>, And<THead, Every<TTail>>, And<THead, Constants.True>>
|
||||
: never;
|
||||
|
||||
/**
|
||||
* The `And` type is a type-level logical conjugation (AND) operator.
|
||||
*
|
||||
* It calculates boolean AND operation of {@link IsTrue} derived from the input types {@link TLeft} and {@link TRight}.
|
||||
*
|
||||
* @template TLeft - The left hand operand of the AND operation. It should extend {@link Constants.BooleanValue}.
|
||||
* @template TRight - The right hand operand of the AND operation. It should extend {@link Constants.BooleanValue}.
|
||||
*
|
||||
* @see {@link IsTrue}
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Constants.True AND Constants.True
|
||||
* type Example1 = And<Constants.True, Constants.True>; // Result: Constants.True
|
||||
*
|
||||
* // Constants.False AND Constants.True
|
||||
* type Example2 = And<Constants.False, Constants.True>; // Result: Constants.False
|
||||
*
|
||||
* // Constants.False AND Constants.False
|
||||
* type Example3 = And<Constants.False, Constants.False>; // Result: Constants.False
|
||||
* ```
|
||||
*/
|
||||
export type And<
|
||||
TLeft extends Constants.BooleanValue,
|
||||
TRight extends Constants.BooleanValue
|
||||
> = IsTrue<IsTrue<TLeft> | IsTrue<TRight>>;
|
||||
|
||||
/**
|
||||
* The `Or` type is a type-level logical conjugation (OR) operator.
|
||||
*
|
||||
* It calculates boolean OR operation of {@link IsTrue} derived from the input types {@link TLeft} and {@link TRight}.
|
||||
*
|
||||
* @template TLeft - The left hand operand of the OR operation. It should extend {@link Constants.BooleanValue}.
|
||||
* @template TRight - The right hand operand of the OR operation. It should extend {@link Constants.BooleanValue}.
|
||||
*
|
||||
* @see {@link IsTrue}
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Constants.True OR Constants.True
|
||||
* type Example1 = Or<Constants.True, Constants.True>; // Result: Constants.True
|
||||
*
|
||||
* // Constants.False OR Constants.True
|
||||
* type Example2 = Or<Constants.False, Constants.True>; // Result: Constants.True
|
||||
*
|
||||
* // Constants.False OR Constants.False
|
||||
* type Example3 = Or<Constants.False, Constants.False>; // Result: Constants.False
|
||||
* ```
|
||||
*/
|
||||
export type Or<TLeft extends Constants.BooleanValue, TRight extends Constants.BooleanValue> = Not<
|
||||
IsFalse<IsTrue<TLeft> | IsTrue<TRight>>
|
||||
>;
|
||||
|
||||
/**
|
||||
* The `Intersect` type constructs a new type by intersecting a list of types.
|
||||
*
|
||||
* @template TValues - The tuple of types to be intersected extending from `unknown[]`.
|
||||
*
|
||||
* @remark This type can easily be replaced by a regular intersection in most scenario.
|
||||
*
|
||||
* The main use-case would be when dealing with a list of types of unknown length.
|
||||
*
|
||||
* In the codebase, it's used mainly for aesthetics reasons (formatting of type params vs intersection members).
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Defining Attribute Options
|
||||
* interface ConfigurableOption {
|
||||
* configurable?: boolean;
|
||||
* }
|
||||
*
|
||||
* interface RequiredOption {
|
||||
* required?: boolean;
|
||||
* }
|
||||
*
|
||||
* // Intersecting Attribute Options
|
||||
* type AttributeOptions = Intersect<
|
||||
* [ ConfigurableOption, RequiredOption ]
|
||||
* >
|
||||
*
|
||||
* // Now `AttributeOptions` contains properties from both `ConfigurableOption` and `RequiredOption`.
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Using `Intersect` to define a complete Attribute type
|
||||
* interface BasicAttribute {
|
||||
* name: string;
|
||||
* type: string;
|
||||
* }
|
||||
*
|
||||
* interface AttributeProperties {
|
||||
* minLength?: number;
|
||||
* maxLength?: number;
|
||||
* }
|
||||
*
|
||||
* type Attribute = Intersect<[
|
||||
* BasicAttribute,
|
||||
* AttributeProperties,
|
||||
* AttributeOptions
|
||||
* ]>
|
||||
*
|
||||
* // Now, `Attribute` type contains
|
||||
* // - name and type fields from `BasicAttribute`
|
||||
* // - minLength and maxLength fields from AttributeProperties
|
||||
* // - configurable and required fields from `AttributeOptions`
|
||||
* ```
|
||||
*/
|
||||
export type Intersect<TValues extends unknown[]> = TValues extends [
|
||||
infer THead,
|
||||
...infer TTail extends unknown[]
|
||||
|
||||
@ -1,2 +1,21 @@
|
||||
/**
|
||||
* Defines a function parameter in a way that accommodates any number and type of arguments.
|
||||
*
|
||||
* The flexibility the type provides makes it a suitable choice for representing generic function in TypeScript whose behaviors heavily rely on the runtime inputs.
|
||||
*
|
||||
* This type is primarily used when the function parameter types and return type can't be accurately defined.
|
||||
*
|
||||
* @remark
|
||||
* It's important to understand that while the {@link Any} type provides flexibility,
|
||||
* it inherently sacrifices the benefits of type-safety.
|
||||
*
|
||||
* Therefore, it's suggested to use it sparingly and only in situations where it's unavoidable.
|
||||
*/
|
||||
export type Any = (...args: any[]) => any;
|
||||
|
||||
/**
|
||||
* Async version of {@link Any}
|
||||
*
|
||||
* @see Any
|
||||
*/
|
||||
export type AnyPromise = (...args: any[]) => Promise<any>;
|
||||
|
||||
@ -1,20 +1,91 @@
|
||||
import type { Array, If, StrictEqual } from '.';
|
||||
|
||||
/**
|
||||
* Assign a default value `TDefault` to `TValue` if `TValue` is of type `never`
|
||||
* Conditionally assigns a fallback type {@link TFallback} to a type {@link TValue}, if {@link TValue} resolves to `never`.
|
||||
*
|
||||
* Otherwise, it assigns the type of {@link TValue}.
|
||||
*
|
||||
* @template TValue - The original type which could be any type or `never`.
|
||||
* @template TFallback - The fallback type that will be assigned to {@link TValue} if {@link TValue} is `never`. It defaults to `unknown`.
|
||||
*
|
||||
* @remark
|
||||
* This type becomes useful when working with conditional types where there are possibilities of ending up with `never` type.
|
||||
*
|
||||
* It provides a way to ensure that, in such situations, the type defaults to a more meaningful type rather than `never`.
|
||||
*
|
||||
* @example
|
||||
* type X = Never<{ foo: 'bar' }, string>
|
||||
* // { foo: 'bar' }
|
||||
* ```typescript
|
||||
* type User = { name: 'John' }
|
||||
*
|
||||
* type X = Never<never>
|
||||
* // unknown
|
||||
* type X = Guard.Never<User>; // X: User
|
||||
* ```
|
||||
*
|
||||
* type X = Never<never, string>
|
||||
* // string
|
||||
* @example
|
||||
* ```typescript
|
||||
* type NoType = never;
|
||||
*
|
||||
* type X = Guard.Never<NoType>; // X: unknown
|
||||
* type Y = Guard.Never<NoType, string>; // Y: string
|
||||
* ```
|
||||
*/
|
||||
export type Never<TValue, TFallback = unknown> = OfTypes<[never], TValue, TFallback>;
|
||||
|
||||
/**
|
||||
* Conditionally assigns a fallback type {@link TFallback} to a type {@link TValue}, if {@link TValue} resolves to `{}`.
|
||||
*
|
||||
* Otherwise, it assigns the type of {@link TValue}.
|
||||
*
|
||||
* @template TValue - The original type which could be any type or `{}`.
|
||||
* @template TFallback - The fallback type that will be assigned to {@link TValue} if {@link TValue} is `{}`. It defaults to `unknown`.
|
||||
*
|
||||
* @remark
|
||||
* This type becomes useful when working with conditional types where there are possibilities of ending up with `{}` type.
|
||||
*
|
||||
* It provides a way to ensure that, in such situations, the type defaults to a more meaningful type rather than `{}`.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type User = { name: 'John' }
|
||||
*
|
||||
* type X = Guard.EmptyObject<User>; // X: User
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type MyObj = {};
|
||||
*
|
||||
* type X = Guard.EmptyObject<MyObj>; // X: unknown
|
||||
* type Y = Guard.EmptyObject<MyObj, string>; // Y: string
|
||||
* ```
|
||||
*/
|
||||
export type EmptyObject<TValue, TFallback = unknown> = OfTypes<[{}], TValue, TFallback>;
|
||||
|
||||
/**
|
||||
* Conditionally assigning a fallback type (@link TFallback) if the value type ({@link TValue}) matches any of the types in {@link TTypes}.
|
||||
*
|
||||
* It basically enables conditional type assignments based on type matching.
|
||||
*
|
||||
* The value is checked against the list of types iteratively. If it matches any type from the list, the fallback type is assigned.
|
||||
*
|
||||
* If it doesn't match, the value's original type is maintained.
|
||||
*
|
||||
* If no fallback is provided, unknown type is used by default.
|
||||
*
|
||||
* @template TTypes - A tuple of types to match the value against. It must extend Array<unknown>
|
||||
* @template TValue - The value whose type is checked against TTypes.
|
||||
* @template TFallback - The type to be assigned if TValue matches any member of TTypes. It defaults to unknown.
|
||||
*
|
||||
* @example
|
||||
* Here, the `TValue` is `string` which exists in the `TTypes` list. Thus, the `TFallback` which is `null` is returned.
|
||||
* ```typescript
|
||||
* type Result = OfTypes<[string, number], string, null>; // Result: null
|
||||
* ```
|
||||
*
|
||||
* Here, the `TValue` is `boolean` which does not exist in the `TTypes` list. Thus, the `TValue` is returned as no match was found.
|
||||
* ```typescript
|
||||
* type Result = OfTypes<[string, number], boolean, number>; // Result: boolean
|
||||
* ```
|
||||
*/
|
||||
export type OfTypes<TTypes extends unknown[], TValue, TFallback = unknown> = TTypes extends [
|
||||
infer THead extends unknown,
|
||||
...infer TTail extends unknown[]
|
||||
@ -25,5 +96,3 @@ export type OfTypes<TTypes extends unknown[], TValue, TFallback = unknown> = TTy
|
||||
If<Array.IsNotEmpty<TTail>, OfTypes<TTail, TValue, TFallback>, TValue>
|
||||
>
|
||||
: never;
|
||||
|
||||
export type EmptyObject<TValue, TFallback = unknown> = OfTypes<[{}], TValue, TFallback>;
|
||||
|
||||
@ -10,33 +10,175 @@ export * from './expression';
|
||||
export * from './json';
|
||||
|
||||
/**
|
||||
* Get the type of a specific key `TKey` in `TValue`
|
||||
* `Get<TValue, TKey>` obtains the type of a specific property (key-value pair) within an object or type.
|
||||
*
|
||||
* @template TValue The initial object type from which a property's type should be extracted.
|
||||
* @template TKey A specific key, existing within `TValue`.
|
||||
*
|
||||
* @example
|
||||
* // Utilizing Get to extract types from an object
|
||||
* type ExampleObject = { foo: 'bar', 'bar': 'foo' };
|
||||
*
|
||||
* type X = Get<{ foo: 'bar', 'bar': 'foo' }, 'foo'>
|
||||
* // 'bar'
|
||||
* // Extract `foo`'s type from the object. This infers and outputs the type 'bar'.
|
||||
* type FooType = Get<ExampleObject, 'foo'>;
|
||||
* let fooVar: FooType = 'bar'; // This is valid
|
||||
*
|
||||
* type X = Get<{ foo: 'bar', 'bar': 'foo' }, 'bar'>
|
||||
* // 'foo'
|
||||
* // Similar extraction for `bar`
|
||||
* type BarType = Get<ExampleObject, 'bar'>;
|
||||
* let barVar: BarType = 'foo'; // This is valid
|
||||
*/
|
||||
export type Get<TValue, TKey extends keyof TValue> = TValue[TKey];
|
||||
|
||||
/**
|
||||
* Represents a simplified version of a given intersection type.
|
||||
* `Simplify` is a type used to flatten intersection types.
|
||||
*
|
||||
* It flattens the properties of every component in the intersection {@link T}, and returns a unified object.
|
||||
* It acts upon each constituent type within the provided intersection, and extracts its properties
|
||||
* to form a new, unified object type where properties do not retain their original type-specific
|
||||
* distinctions.
|
||||
*
|
||||
* @template T The original type to be simplified.
|
||||
* It’s useful when there's a need to treat an intersection type as a single unified object type,
|
||||
* while ensuring that the properties of each component type in the intersection are accounted for.
|
||||
*
|
||||
* @template T The type parameter indicating the intersection type that is to be simplified. This must extend `unknown`.
|
||||
*
|
||||
* @example
|
||||
* Consider the following example with two distinct types `A` and `B`:
|
||||
* ```typescript
|
||||
* type A = { a: number };
|
||||
* type B = { b: string };
|
||||
* ```
|
||||
* If we were to create an intersection of these two types as `C`:
|
||||
* ```typescript
|
||||
* type C = A & B;
|
||||
* ```
|
||||
* The usual operations on `C` would account for the distinct types `A` and `B`. However, when we want to treat it as a single unified object type, we can apply `Simplify` in the following way:
|
||||
* ```typescript
|
||||
* type D = Simplify<C>;
|
||||
* ```
|
||||
* Now, `D` is a single object type with properties from both `A` and `B`, and we can operate on it as:
|
||||
* ```typescript
|
||||
* let obj: D = { a: 5, b: 'hello' };
|
||||
* ```
|
||||
*
|
||||
* @remark
|
||||
* While this type is beneficial in certain contexts where treating intersection types as single unified objects is desirable (e.g. when exposing
|
||||
* complex types to end-users), it's important to remember that it strips the original type information from the properties.
|
||||
*
|
||||
* Thus, it may not be suitable in situations where retaining the distinction between types present in the intersection is important.
|
||||
*/
|
||||
export type Simplify<T extends unknown> = { [TKey in keyof T]: T[TKey] };
|
||||
|
||||
/**
|
||||
* Utility type that creates a new type by excluding properties from the left type ({@link TLeft}) that exist in the right type ({@link TRight}).
|
||||
*
|
||||
* @template TLeft
|
||||
* @template TRight
|
||||
*
|
||||
* @example
|
||||
* type User = {
|
||||
* id: number,
|
||||
* name: string,
|
||||
* email: string,
|
||||
* };
|
||||
*
|
||||
* type Credentials = {
|
||||
* email: string,
|
||||
* password: string,
|
||||
* };
|
||||
*
|
||||
* type UserWithoutCredentials = Without<User, Credentials>;
|
||||
*
|
||||
* const user: UserWithoutCredentials = {
|
||||
* id: 1,
|
||||
* name: 'Alice'
|
||||
* // no email property because it's excluded by the Without type
|
||||
* };
|
||||
*/
|
||||
export type Without<TLeft, TRight> = { [key in Exclude<keyof TLeft, keyof TRight>]?: never };
|
||||
|
||||
/**
|
||||
* Creates a type that is mutually exclusive between two given types.
|
||||
*
|
||||
* @template TLeft - The first type.
|
||||
* @template TRight - The second type.
|
||||
*
|
||||
* @remarks
|
||||
* This type is used to create a type that can be either TLeft or TRight, but not both at the same time.
|
||||
*
|
||||
* @example
|
||||
* // Example 1: XOR type with two object types
|
||||
* type A = { a: number };
|
||||
* type B = { b: string };
|
||||
*
|
||||
* const value1: XOR<A, B> = { a: 1 }; // Valid, TLeft type A is assigned
|
||||
* const value2: XOR<A, B> = { b: "hello" }; // Valid, TRight type B is assigned
|
||||
* const value3: XOR<A, B> = { a: 1, b: "hello" }; // Invalid, both types A and B cannot be assigned at the same time
|
||||
*
|
||||
* // Example 2: XOR type with object type and string type
|
||||
* type C = XOR<A, string>;
|
||||
*
|
||||
* const value4: C = { a: 1 }; // Valid, object type A is assigned
|
||||
* const value5: C = "hello"; // Valid, string type is assigned
|
||||
* const value6: C = { a: 1, b: "hello" }; // Invalid, both object type A and string type cannot be assigned at the same time
|
||||
*/
|
||||
export type XOR<TLeft, TRight> = TLeft | TRight extends object
|
||||
? (Without<TLeft, TRight> & TRight) | (Without<TRight, TLeft> & TLeft)
|
||||
: TLeft | TRight;
|
||||
|
||||
/**
|
||||
* The `Cast` type is designed for casting a value of type {@link TValue} into type {@link TType}, thus making sure {@link TValue} extends {@link TType}.
|
||||
*
|
||||
* If the casting is impossible ({@link TValue} does not extend {@link TType}), it returns `never`.
|
||||
*
|
||||
* @template TValue - The type to cast.
|
||||
* @template TType - The target type.
|
||||
*
|
||||
* @example
|
||||
* // In this example, the String 'Hello' is attempted to be cast to a number,
|
||||
* // which is not possible. Thus, the result would be 'never'.
|
||||
* type ImpossibleCasting = Cast<'Hello', number>; // this will be 'never'
|
||||
*
|
||||
* @example
|
||||
* // In this example, the String 'Hello' is attempted to be cast to a String,
|
||||
* // which is possible. Thus, the result would be 'Hello'.
|
||||
* type PossibleCasting = Cast<'Hello', string>; // this will be 'Hello'
|
||||
*
|
||||
*/
|
||||
export type Cast<TValue, TType> = TValue extends TType ? TValue : never;
|
||||
|
||||
/**
|
||||
* The `PartialWithThis<T>` type extends the functionality of two types: {@link Partial} and {@link ThisType}.
|
||||
*
|
||||
* It creates a type that represents an object with a subset of properties from the provided
|
||||
* type {@link T} merged with a pseudo `this` context for methods, based on the same type parameter.
|
||||
*
|
||||
* - {@link Partial} makes all properties of the given type optional.
|
||||
* - {@link ThisType} defines what `this` refers to within a method of the final object.
|
||||
*
|
||||
* @template T The type to create a subset from and to use for the pseudo 'this' context.
|
||||
*
|
||||
* It can be any TypeScript type such as interface, class, primitive, or even another
|
||||
* generic type.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* interface MyObject {
|
||||
* property1: string;
|
||||
* property2: number;
|
||||
* method(): void;
|
||||
* }
|
||||
*
|
||||
* let foo: PartialWithThis<MyObject> = {
|
||||
* property1: 'Hello',
|
||||
* method() {
|
||||
* // Here, `this` refers to `MyObject`
|
||||
* console.log(this.property1);
|
||||
* },
|
||||
* // `property2` is optional
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @remark
|
||||
* This type can be useful when working with partial data and object methods that contain a pseudo `this` context.
|
||||
*/
|
||||
export type PartialWithThis<T> = Partial<T> & ThisType<T>;
|
||||
|
||||
@ -1,9 +1,121 @@
|
||||
/**
|
||||
* The `JSONValue` type embodies all potential JSON data forms ({@link JSONPrimitive}, {@link JSONObject}, and {@link JSONArray}).
|
||||
*
|
||||
* @note `JSONValue` does not introduce any new type parameters; it merely aggregates {@link JSONPrimitive}, {@link JSONObject}, and {@link JSONArray}.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* function processJSON(jsonData: JSONValue): void {
|
||||
* if (Array.isArray(jsonData)) {
|
||||
* console.log('This is a JSONArray: ', JSON.stringify(jsonData, null, 2));
|
||||
* } else if (typeof jsonData === 'object') {
|
||||
* console.log('This is a JSONObject: ', JSON.stringify(jsonData, null, 2));
|
||||
* } else {
|
||||
* console.log('This is a JSONPrimitive: ', jsonData);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* processJSON(['hello', { anotherKey: 'anotherValue' }]);
|
||||
* // This is a JSONArray: ["hello", { "anotherKey": "anotherValue" }]
|
||||
*
|
||||
* processJSON({ key: 'value' });
|
||||
* // This is a JSONObject: { "key": "value" }
|
||||
*
|
||||
* processJSON(10);
|
||||
* // This is a JSONPrimitive: 10
|
||||
* ```
|
||||
*
|
||||
* @see {@link JSONPrimitive} - The simplest form of `JSONValue`, corresponds to basic JSON data types.
|
||||
* @see {@link JSONObject} - A potential `JSONValue`, encapsulates JSON object structures.
|
||||
* @see {@link JSONArray} - A potential `JSONValue`, encapsulates JSON arrays.
|
||||
*/
|
||||
export type JSONValue = JSONPrimitive | JSONObject | JSONArray;
|
||||
|
||||
/**
|
||||
* The `JSONPrimitive` type models the fundamental data types (`string`, `number`, `boolean`, `null`) of JSON in TypeScript.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* declare function set(key: string, value: JSONPrimitive): void;
|
||||
*
|
||||
* set('string', 'foo'); // This is valid
|
||||
* set('number', 42); // This is valid
|
||||
* set('boolean', true); // This is valid
|
||||
* set('null', null); // This is valid
|
||||
* set('array', []); // Error
|
||||
* set('undefined', undefined); // Error
|
||||
* ```
|
||||
*
|
||||
* @see {@link JSONValue} - The potential forms of JSON data, including `JSONPrimitive`.
|
||||
* @see {@link JSONObject} - Incorporated in {@link JSONValue}, represents JSON objects.
|
||||
* @see {@link JSONArray} - Incorporated in {@link JSONValue}, represents JSON arrays.
|
||||
*
|
||||
* @remarks
|
||||
* `JSONPrimitive` provides a foundation for describing JSON data.
|
||||
* Combined with {@link JSONObject} and {@link JSONArray}, they encompass all possible JSON data types.
|
||||
*/
|
||||
export type JSONPrimitive = string | number | boolean | null;
|
||||
|
||||
/**
|
||||
* The `JSONArray` type models a standard JSON array in TypeScript, which allows manipulation of arrays of {@link JSONValue} elements.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Create a JSONArray consisting of different JSONValues
|
||||
* let jsonArray: JSONArray = ['hello', 10, true, null, {key: 'value'}, ['nested array']];
|
||||
*
|
||||
* function prettyPrint(jsonArray: JSONArray): void {
|
||||
* jsonArray.forEach(item => {
|
||||
* if(typeof item === 'object' && item !== null) {
|
||||
* // If it's a JSONObject or another JSONArray, stringify it
|
||||
* console.log(JSON.stringify(item, null, 2));
|
||||
* } else {
|
||||
* // If it's a JSONPrimitive, print it directly
|
||||
* console.log(item);
|
||||
* }
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* prettyPrint(jsonArray); // Will print all items in a friendly format to the console
|
||||
* ```
|
||||
*
|
||||
* This type is part of a series of types used for modeling all possible JSON values in TypeScript.
|
||||
* @see {@link JSONValue} - The supertype of all elements that a `JSONArray` can contain.
|
||||
* @see {@link JSONPrimitive} - The simplest kind of `JSONValue` that a `JSONArray` can contain.
|
||||
* @see {@link JSONObject} - A possible `JSONValue` that a `JSONArray` can contain.
|
||||
*
|
||||
* @remarks
|
||||
* The `JSONArray` is a versatile type that can contain various kinds of JSON data, even nested arrays or objects.
|
||||
*/
|
||||
export type JSONArray = Array<JSONValue>;
|
||||
|
||||
/**
|
||||
* The `JSONObject` interface formally defines a JavaScript object with string keys and values of type {@link JSONValue}.
|
||||
*
|
||||
* It models standard JSON objects as TypeScript types, facilitating their manipulation.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* function addToJSONObject(key: string, value: JSONValue, jsonObject: JSONObject): JSONObject {
|
||||
* // Copy the existing JSONObject
|
||||
* let updatedObject: JSONObject = { ...jsonObject };
|
||||
*
|
||||
* // Add the new key-value pair
|
||||
* updatedObject[key] = value;
|
||||
*
|
||||
* // Return the updated JSONObject
|
||||
* return updatedObject;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see {@link JSONValue} - The permitted types for values within the `JSONObject` (primitives, objects, or arrays).
|
||||
* @see {@link JSONPrimitive} - The basis for JSON data, used in {@link JSONValue}.
|
||||
* @see {@link JSONArray} - Arrays used in {@link JSONValue}.
|
||||
*
|
||||
* @remarks
|
||||
* The keys of the `JSONObject` are always of type string, as per the standard JSON specification.
|
||||
* Values may take any valid {@link JSONValue}, allowing nested data structures.
|
||||
*/
|
||||
export interface JSONObject {
|
||||
[key: string]: JSONValue;
|
||||
}
|
||||
|
||||
@ -1,64 +1,164 @@
|
||||
/**
|
||||
* Retrieve object's (`TValue`) keys if they extend the given `TTest` type.
|
||||
* Extracts object's (`TValue`) keys where the key's value type extends the given `TTest` type.
|
||||
*
|
||||
* @template TValue - The original object type.
|
||||
* @template TTest - The test type. Keys of TValue having values that extend this type are extracted.
|
||||
* @template TExtract - An optional constraint for the keys of TValue.
|
||||
*
|
||||
* @example
|
||||
* type X = KeysBy<{ foo: 'bar', bar: 'foo', foobar: 2 }, string>
|
||||
* // 'foo' | 'bar'
|
||||
*
|
||||
* type Base = { x: 'foo' | 'bar' };
|
||||
* type Obj = { foo: { x: 'foo' }, bar: { x: 'bar' }, other: { x: '42' } };
|
||||
* type X = KeysBy<Obj, Base>
|
||||
* // 'foo' | 'bar'
|
||||
* // Here TValue is `{ foo: 'bar', bar: 'foo', foobar: 2 }` and TTest is `string`.
|
||||
* // So it extracts keys `foo` and `bar`, because their values are string type.
|
||||
* type keys = KeysBy<{ foo: 'bar', bar: 'foo', foobar: 2 }, string> // 'foo' | 'bar'
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* // Here TValue is `{ foo: { x: 'foo' }, bar: { x: 'bar' }, other: { x: '42' } }` and TTest is `{ x: 'foo' | 'bar' }`.
|
||||
* // So it extracts keys `foo` and `bar`, because their values are extending `{ x: 'foo' | 'bar' }`.
|
||||
* type Base = { x: 'foo' | 'bar' };
|
||||
* type Obj = { foo: { x: 'foo' }, bar: { x: 'bar' }, other: { x: '42' } };
|
||||
* type X = KeysBy<Obj, Base> // 'foo' | 'bar'
|
||||
*
|
||||
* @see {@link KeysExcept}
|
||||
* @see {@link PickBy}
|
||||
*/
|
||||
export type KeysBy<TValue, TTest, TExtract extends keyof any = keyof any> = {
|
||||
[key in keyof TValue & TExtract]: TValue[key] extends TTest ? key : never;
|
||||
}[keyof TValue & TExtract];
|
||||
|
||||
/**
|
||||
* Retrieve object's (`TValue`) keys if they don't extend the given `TTest` type.
|
||||
* Extracts the keys of a given object ({@link TValue}). It includes only those keys which do not map to a value of type {@link TTest}`.
|
||||
*
|
||||
* @template TValue - The object whose keys are to be examined and selectively retrieved
|
||||
* @template TTest - The type of value to be used as an exclusion criterion for selecting keys from `TValue`
|
||||
* @template TExtract - An optional union of keys to constrain the keys that are being examined. If not provided, it defaults to examining all keys in `TValue`.
|
||||
*
|
||||
* @example
|
||||
* type X = KeysExcept<{ foo: 'bar', bar: 'foo', foobar: 2 }, string>
|
||||
* // foobar
|
||||
* ```typescript
|
||||
* // In this example, KeysExcept is used to fetch keys from the object which do not have string type values
|
||||
* type ExampleType = { foo: 'bar', bar: 'foo', foobar: 2 }
|
||||
* type ResultType = KeysExcept<ExampleType, string>
|
||||
* // The resulting type is "foobar"
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In this example, we use a base type to define allowed value types and only fetch those keys from the object that have values not extending the base type
|
||||
* type Base = { x: 'foo' | 'bar' };
|
||||
* type Obj = { foo: { x: 'foo' }, bar: { x: 'bar' }, other: { x: '42' } };
|
||||
* type X = KeysBy<Obj, Base>
|
||||
* // 'other'
|
||||
* // The resulting type is "other"
|
||||
* ```
|
||||
*/
|
||||
export type KeysExcept<TValue, TTest, TExtract extends keyof any = keyof any> = {
|
||||
[key in keyof TValue & TExtract]: TValue[key] extends TTest ? never : key;
|
||||
}[keyof TValue & TExtract];
|
||||
|
||||
/**
|
||||
* Retrieve object's (`TValue`) properties if their value extends the given `TTest` type.
|
||||
* Select properties from an object ({@link TValue}), only if their types extend a specific test type ({@link TTest}).
|
||||
*
|
||||
* @template TValue - The object type from which properties are selected.
|
||||
* @template TTest - The test type. Properties of TValue extending this type are selected.
|
||||
*
|
||||
* @example
|
||||
* type X = KeysBy<{ foo: 'bar', bar: 'foo', foobar: 2 }, string>
|
||||
* // { foo: 'bar', bar: 'foo' }
|
||||
*
|
||||
* type Base = { x: 'foo' | 'bar' };
|
||||
* type Obj = { foo: { x: 'foo' }, bar: { x: 'bar' }, other: { x: '42' } };
|
||||
* type X = KeysBy<Obj, Base>
|
||||
* // { foo: { x: 'foo' }, bar: { x: 'bar' } }
|
||||
* // If we have this:
|
||||
* type FruitAttributes = { color: string, taste: string, weight: number, isOrganic: boolean };
|
||||
*
|
||||
* // And we use `PickBy` like so:
|
||||
* type StringAttributes = PickBy<FruitAttributes, string>;
|
||||
*
|
||||
* // Then, `StringAttributes` will equal: `{ color: string, taste: string }`
|
||||
*/
|
||||
export type PickBy<TValue, TTest> = Pick<TValue, KeysBy<TValue, TTest>>;
|
||||
|
||||
/**
|
||||
* Creates a new type from a given type ({@link TValue}), and select specific
|
||||
* keys ({@link TKeys}) to be optionally present in the new type.
|
||||
*
|
||||
* @template TValue The original type of object.
|
||||
* @template TKeys A union of selected {@link TValue} object keys that should be partial/optional in the new type.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type Person = {
|
||||
* name: string;
|
||||
* age: number;
|
||||
* };
|
||||
*
|
||||
* type PartialAgePerson = PartialBy<Person, 'age'>;
|
||||
*
|
||||
* // the type PartialAgePerson is now equivalent to:
|
||||
* // {
|
||||
* // name: string;
|
||||
* // age?: number;
|
||||
* // }
|
||||
* ```
|
||||
*/
|
||||
export type PartialBy<TValue, TKeys extends keyof TValue> = Omit<TValue, TKeys> &
|
||||
Partial<Pick<TValue, TKeys>>;
|
||||
|
||||
/**
|
||||
* Retrieve object's (`TObject`) values
|
||||
* Extracts all unique values from a given object as a union type.
|
||||
*
|
||||
* @template TObject - An object from which values are to be extracted. It must extend the `object` type.
|
||||
*
|
||||
* @remark
|
||||
* It works with non-primitive values as well. Hence, if a value is an object, it is included as is. Primitive types are included directly.
|
||||
*
|
||||
* @example
|
||||
* type X = Values<{ foo: 'bar', bar: 'foo', foobar: 2 }>
|
||||
* // 'bar' | 'foo' | 2
|
||||
* With a simple object:
|
||||
* ```TypeScript
|
||||
* type SimpleExample = Values<{
|
||||
* a: 'one',
|
||||
* b: 'two',
|
||||
* c: 3
|
||||
* }>;
|
||||
* // Result: 'one' | 'two' | 3
|
||||
* ```
|
||||
*
|
||||
* type Y = Values<{ foo: { x: 'foo' }, bar: { x: 'bar' }, other: { x: '42' } }>
|
||||
* // { x: 'foo' } | { x: 'bar' } | { x: '42' }
|
||||
* @example
|
||||
* With complex (non-primitive) values in an object:
|
||||
* ```TypeScript
|
||||
* type ComplexExample = Values<{
|
||||
* a: { x: 10 },
|
||||
* b: { y: 'twenty' },
|
||||
* c: { z: true }
|
||||
* }>;
|
||||
* // Result: { x: 10 } | { y: 'twenty' } | { z: true }
|
||||
* ```
|
||||
*/
|
||||
export type Values<TObject extends object> = TObject[keyof TObject];
|
||||
|
||||
/**
|
||||
* Provides a way to set deeply-nested properties within `TObject` to optional.
|
||||
*
|
||||
* @template TObject Type of the object that will become deeply partial.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* interface Person {
|
||||
* name: string;
|
||||
* age: number;
|
||||
* address: {
|
||||
* city: string;
|
||||
* street: string;
|
||||
* postalCode: number;
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* const partialPerson: DeepPartial<Person> = {}; // This is now valid
|
||||
*
|
||||
* // You can assign partially filled objects
|
||||
* const anotherPerson: DeepPartial<Person> = {
|
||||
* name: 'John',
|
||||
* address: {
|
||||
* city: 'San Francisco',
|
||||
* // street and postal code are optional
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export type DeepPartial<TObject> = TObject extends object
|
||||
? {
|
||||
[TKey in keyof TObject]?: DeepPartial<TObject[TKey]>;
|
||||
@ -66,11 +166,22 @@ export type DeepPartial<TObject> = TObject extends object
|
||||
: TObject;
|
||||
|
||||
/**
|
||||
* Replace the keys of an object with the keys of another object
|
||||
* Creates a new type by replacing properties of {@link TObject} with properties from {@link TNew}.
|
||||
*
|
||||
* This is particularly useful to fine-tune the shape of an object type by altering some of its properties while keeping the rest intact.
|
||||
*
|
||||
* @template TObject - A type that extends `object`. This should be the original type that you intend to transform.
|
||||
* @template TNew - A partial type of {@link TObject} where keys are replaced with new types.
|
||||
*
|
||||
* @example
|
||||
* type X = Replace<{ foo: number, bar: number}, { foo: string }>
|
||||
* // { foo: string, bar: number }
|
||||
*
|
||||
* ```typescript
|
||||
* type Original = { foo: number, bar: number}; // Declare original type
|
||||
* type Transformation = { foo: string }; // Declare keys to replace from original type
|
||||
*
|
||||
* type Result = Replace<Original, Transformation>;
|
||||
* // The transformed type now becomes { foo: string, bar: number }
|
||||
* ```
|
||||
*/
|
||||
export type Replace<
|
||||
TObject extends object,
|
||||
|
||||
@ -2,48 +2,140 @@ import type { Extends } from './expression';
|
||||
|
||||
/**
|
||||
* Alias for any literal type (useful for template string parameters)
|
||||
*
|
||||
* @see {@link Split}
|
||||
* @see {@link Suffix}
|
||||
* @see {@link Prefix}
|
||||
* @see {@link StartsWith}
|
||||
* @see {@link EndsWith}
|
||||
*/
|
||||
export type Literal = string | number | bigint | boolean;
|
||||
|
||||
/**
|
||||
* Used to check if a string includes a given literal
|
||||
*/
|
||||
export type Includes<T extends Literal> = `${string}${T}${string}`;
|
||||
|
||||
/**
|
||||
* Used to make sure the given string is not empty
|
||||
* Ensures that a string is not empty.
|
||||
*
|
||||
* @template T - The type that extends a string.
|
||||
*
|
||||
* @example
|
||||
* // T is assigned a string type
|
||||
* type A = NonEmpty<string>;
|
||||
* // A can be any string except the empty string
|
||||
* let a: A = "hello"; // Valid
|
||||
* a = ""; // Error: Type '""' is not assignable to type 'NonEmpty<string>'
|
||||
*
|
||||
* @example
|
||||
* // T is assigned a string literal type
|
||||
* type B = NonEmpty<"hello">;
|
||||
* // B can only be "hello"
|
||||
* let b: B = "hello"; // Valid
|
||||
* b = ""; // Error: Type '""' is not assignable to type 'NonEmpty<"hello">'
|
||||
*/
|
||||
export type NonEmpty<T extends string> = T extends '' ? never : T;
|
||||
|
||||
/**
|
||||
* Split the given string into a tuple using the given `TSeparator` literal
|
||||
* Splits a given string ({@link TValue}) around occurrences of a given separator ({@link TSeparator}).
|
||||
*
|
||||
* The resulting type is an array where each item is a part of the original string that falls between two instances of the separator.
|
||||
*
|
||||
* If the string does not contain the separator, the array will contain just the original string.
|
||||
*
|
||||
* If the string is empty, the result is an empty array.
|
||||
*
|
||||
* @template TValue - The string to split. Must extend `string`.
|
||||
* @template TSeparator - The character(s) used to determine in which locations the string should be split. Must be a `string`.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```typescript
|
||||
* type Example = Split<'a.b.c.d', '.'>; // Output will be: ['a', 'b', 'c', 'd']
|
||||
* ```
|
||||
* In the above example, the string 'a.b.c.d' is split around occurrences of the '.' separator.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```typescript
|
||||
* type ExampleUnion = Split<'a.b-c', '.' | '-'>; // Output will be: ['a', 'b-c'] | ['a.b', 'c']
|
||||
* ```
|
||||
* The split operation will distribute the union members and create two possible return type for the union
|
||||
*/
|
||||
export type Split<
|
||||
TValue extends string,
|
||||
TSeparator extends Literal
|
||||
> = TValue extends `${infer TLeft}${TSeparator}${infer TRight}`
|
||||
? [TLeft, ...Split<TRight, TSeparator>]
|
||||
: TValue extends ''
|
||||
? []
|
||||
: [TValue];
|
||||
export type Split<TValue extends string, TSeparator extends string> = {
|
||||
[TSep in TSeparator]: TValue extends `${infer TLeft}${TSep}${infer TRight}`
|
||||
? [TLeft, ...Split<TRight, TSep>]
|
||||
: TValue extends ''
|
||||
? []
|
||||
: [TValue];
|
||||
}[TSeparator];
|
||||
|
||||
/**
|
||||
* Add a literal suffix (`TSuffix`) at the end of the given string
|
||||
* The `Suffix` type appends a literal suffix ({@link TSuffix}) to the end of a provided string ({@link TValue}).
|
||||
*
|
||||
* @template TValue - The string to add the suffix to.
|
||||
* @template TSuffix - It extends the {@link Literal} type, and represents the suffix to append.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // A type that appends '.com' to a string
|
||||
* type DomainName = Suffix<string, '.com'>;
|
||||
* const myComDomain: DomainName = 'example.com'; // This is valid
|
||||
* const myNetDomain: DomainName = 'example.net'; // This is not valid
|
||||
*
|
||||
* // A variant using `number` as literal
|
||||
* type SuffixedNumber = Suffix<string, 1>;
|
||||
* const mySuffixedNumber: SuffixedNumber = 'example1'; // Also valid
|
||||
* ```
|
||||
*/
|
||||
export type Suffix<TValue extends string, TSuffix extends Literal> = `${TValue}${TSuffix}`;
|
||||
|
||||
/**
|
||||
* Add a literal prefix (`TPrefix`) at the beginning of the given string
|
||||
* Prepend a literal prefix ({@link TPrefix}) to the start of a provided string ({@link TValue}).
|
||||
*
|
||||
* @template TValue - The string to add the prefix to.
|
||||
* @template TPrefix - It extends the {@link Literal} type, and represents the prefix to prepend.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // A type that prepends 'Hello ' to a string
|
||||
* type Greeting = Suffix<string, 'Hello '>;
|
||||
* const greeting: Greeting = 'Hello Bob'; // This is valid
|
||||
* const farewell: Greeting = 'Bye Bob'; // This is not valid
|
||||
* ```
|
||||
*/
|
||||
export type Prefix<TValue extends string, TPrefix extends Literal> = `${TPrefix}${TValue}`;
|
||||
|
||||
/**
|
||||
* Creates an indexed object where every key is a string and every value is `T`
|
||||
*
|
||||
* @template T - Value type of the dictionary
|
||||
*
|
||||
* @example
|
||||
* // Dictionary where each key is a string and is bound to a number type value.
|
||||
* const numDict: Dict<number> = {
|
||||
* 'a': 1,
|
||||
* 'b': 2,
|
||||
* 'c': 3
|
||||
* };
|
||||
*/
|
||||
export type Dict<T> = { [key: string]: T };
|
||||
|
||||
/**
|
||||
* Checks if a given string ends with the given literal
|
||||
* Determines if a string, represented by {@link TValue}, ends with a specific literal ({@link TSuffix}).
|
||||
*
|
||||
* @template TValue - The string to check.
|
||||
* @template TSuffix - A literal which may or may not be at the end of {@link TValue}.
|
||||
*
|
||||
* @remark
|
||||
* To remember easily: `String.prototype.endsWith` method but at type level.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type Result = EndsWith<"HelloWorld", "World">;
|
||||
* // Output: Constants.True
|
||||
* ```
|
||||
*
|
||||
* ```typescript
|
||||
* type Result = EndsWith<"HelloWorld", "Hello">;
|
||||
* // Output: Constants.False
|
||||
* ```
|
||||
*/
|
||||
export type EndsWith<TValue extends string, TSuffix extends Literal> = Extends<
|
||||
TValue,
|
||||
@ -51,7 +143,25 @@ export type EndsWith<TValue extends string, TSuffix extends Literal> = Extends<
|
||||
>;
|
||||
|
||||
/**
|
||||
* Checks if a given string starts with the given literal
|
||||
* Determines if a string, represented by {@link TValue}, starts with a specific literal ({@link TPrefix}).
|
||||
*
|
||||
* @template TValue - The string to check.
|
||||
* @template TPrefix - A literal which may or may not be at the start of {@link TValue}.
|
||||
*
|
||||
* @remark
|
||||
* To remember easily: `String.prototype.startsWith` method but at type level.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type IsHelloWorld = StartsWith<"Hello World", "Hello">;
|
||||
* // Output: Constants.True
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type NotHelloWorld = StartsWith<"World Hello", "Hello">;
|
||||
* // Output: Constants.False
|
||||
* ```
|
||||
*/
|
||||
export type StartsWith<TValue extends string, TPrefix extends Literal> = Extends<
|
||||
TValue,
|
||||
|
||||
@ -1,7 +1,17 @@
|
||||
import type { Literal } from './string';
|
||||
|
||||
/**
|
||||
* Aggregate the given tuple into a string, separated by the given `TSeparator` literal
|
||||
* Transforms a tuple ({@link TCollection}) into a concatenated string, interlaced with a designated separator character ({@link TSeparator}).
|
||||
*
|
||||
* @template TCollection - Represents the array of elements to be joined.
|
||||
* @template TSeparator - Represents the separator character used to join the elements of {@link TCollection}.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type R1 = Join<["John", "Doe", "Smith"], ",">; // type R1 = "John,Doe,Smith"
|
||||
* type R2 = Join<[1, 2, 3, 4], "-">; // type R2 = "1-2-3-4"
|
||||
* type R3 = Join<["Foo", "Bar", "Baz"], "," | "-">; // type R3 = "Foo,Bar,Baz" | "Foo-Bar-Baz"
|
||||
* ```
|
||||
*/
|
||||
export type Join<TCollection extends unknown[], TSeparator extends Literal> = TCollection extends [
|
||||
infer THead extends Literal,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user