type-fest
Advanced tools
| import type {IsAny} from './is-any.d.ts'; | ||
| import type {If} from './if.d.ts'; | ||
| import type {IsEqual} from './is-equal.d.ts'; | ||
| import type {IfNotAnyOrNever} from './internal/type.d.ts'; | ||
| /** | ||
| A stricter version of `Extract<T, U>` that extracts types only when they are exactly identical. | ||
| @example | ||
| ``` | ||
| import type {ExtractExactly} from 'type-fest'; | ||
| type TestExtract1 = Extract<'a' | 'b' | 'c' | 1 | 2 | 3, string>; | ||
| //=> 'a' | 'b' | 'c' | ||
| type TestExtractExactly1 = ExtractExactly<'a' | 'b' | 'c' | 1 | 2 | 3, string>; | ||
| //=> never | ||
| type TestExtract2 = Extract<'a' | 'b' | 'c' | 1 | 2 | 3, any>; | ||
| //=> 'a' | 'b' | 'c' | 1 | 2 | 3 | ||
| type TestExtractExactly2 = ExtractExactly<'a' | 'b' | 'c' | 1 | 2 | 3, any>; | ||
| //=> never | ||
| type TestExtract3 = Extract<{a: string} | {a: string; b: string}, {a: string}>; | ||
| //=> {a: string} | {a: string; b: string} | ||
| type TestExtractExactly3 = ExtractExactly<{a: string} | {a: string; b: string}, {a: string}>; | ||
| //=> {a: string} | ||
| ``` | ||
| @category Improved Built-in | ||
| */ | ||
| export type ExtractExactly<Union, Match> = | ||
| IfNotAnyOrNever<Union, { | ||
| ifNot: _ExtractExactly<Union, Match>; | ||
| // If `Union` is `any`, then if `Match` is `any`, return `any`, else return `never`. | ||
| ifAny: If<IsAny<Match>, Union, never>; | ||
| // If `Union` is `never`, return `never`, doesn't matter what `Match` is. | ||
| ifNever: never; | ||
| }>; | ||
| type _ExtractExactly<Union, Match> = | ||
| IfNotAnyOrNever<Match, { | ||
| ifNot: Union extends unknown // For distributing `Union` | ||
| ? [Match extends unknown // For distributing `Match` | ||
| ? If<IsEqual<Union, Match>, true, never> | ||
| : never] extends [never] ? never : Union | ||
| : never; | ||
| // If `Match` is `any` or `never`, then return `never`, | ||
| // because `Union` cannot be `any` or `never` here. | ||
| ifAny: never; | ||
| ifNever: never; | ||
| }>; | ||
| export {}; |
| import type {StringToArray} from './string-to-array.d.ts'; | ||
| /** | ||
| Returns the length of the given string. | ||
| @example | ||
| ``` | ||
| import type {StringLength} from 'type-fest'; | ||
| type A = StringLength<'abcde'>; | ||
| //=> 5 | ||
| type B = StringLength<'abcde' | 'fgh'>; | ||
| //=> 3 | 5 | ||
| ``` | ||
| For non-literal strings, the result is `number` because the length of a non-literal string can be any number. | ||
| @example | ||
| ``` | ||
| import type {StringLength} from 'type-fest'; | ||
| type A = StringLength<string>; | ||
| //=> number | ||
| type B = StringLength<Uppercase<string>>; | ||
| //=> number | ||
| type C = StringLength<`${string}abc`>; | ||
| //=> number | ||
| ``` | ||
| @category String | ||
| @category Template literal | ||
| */ | ||
| export type StringLength<S extends string> = StringToArray<S>['length']; | ||
| export {}; |
| import type {ApplyDefaultOptions} from './internal/object.d.ts'; | ||
| import type {IfNotAnyOrNever} from './internal/type.d.ts'; | ||
| import type {IsStringLiteral} from './is-literal.d.ts'; | ||
| import type {Or} from './or.d.ts'; | ||
| /** | ||
| @see {@link StringToArray} | ||
| */ | ||
| export type StringToArrayOptions = { | ||
| /** | ||
| When enabled, non-literal parts of the string (e.g., `string`, `Uppercase<string>`) are mapped as single elements instead of being mapped as a rest element. | ||
| Note: Enabling this option can produce misleading results that might not reflect the actual runtime behavior. | ||
| For example, `StringToArray<string, {mapNonLiteralsDirectly: true}>` returns `[string]`, but at runtime, the string could be `'abc'` (which satisfies `string`), and converting it to an array would result in `['a', 'b', 'c']`, which doesn't satisfy `[string]`. | ||
| So, it is recommended to not enable this option unless you are aware of the implications. | ||
| @default false | ||
| @example | ||
| ``` | ||
| import type {StringToArray} from 'type-fest'; | ||
| type A = StringToArray<string, {mapNonLiteralsDirectly: false}>; | ||
| //=> string[] | ||
| type B = StringToArray<string, {mapNonLiteralsDirectly: true}>; | ||
| //=> [string] | ||
| type C = StringToArray<`on${string}`, {mapNonLiteralsDirectly: false}>; | ||
| //=> ['o', 'n', ...string[]] | ||
| type D = StringToArray<`on${string}`, {mapNonLiteralsDirectly: true}>; | ||
| //=> ['o', 'n', string] | ||
| type E = StringToArray<`${string}xyz`, {mapNonLiteralsDirectly: false}>; | ||
| //=> [...string[], 'x', 'y', 'z'] | ||
| type F = StringToArray<`${string}xyz`, {mapNonLiteralsDirectly: true}>; | ||
| //=> [string, 'x', 'y', 'z'] | ||
| ``` | ||
| */ | ||
| mapNonLiteralsDirectly?: boolean; | ||
| }; | ||
| type DefaultStringToArrayOptions = { | ||
| mapNonLiteralsDirectly: false; | ||
| }; | ||
| /** | ||
| Returns an array of the characters of the specified string. | ||
| @example | ||
| ``` | ||
| import type {StringToArray} from 'type-fest'; | ||
| type A = StringToArray<'abcde'>; | ||
| //=> ['a', 'b', 'c', 'd', 'e'] | ||
| type B = StringToArray<''>; | ||
| //=> [] | ||
| type C = StringToArray<string>; | ||
| //=> string[] | ||
| type D = StringToArray<`foo${string}bar`>; | ||
| //=> ['f', 'o', 'o', ...string[], 'b', 'a', 'r'] | ||
| type E = StringToArray<`foo${string}bar`, {mapNonLiteralsDirectly: true}>; | ||
| //=> ['f', 'o', 'o', string, 'b', 'a', 'r'] | ||
| ``` | ||
| @see {@link StringToArrayOptions} | ||
| @category String | ||
| */ | ||
| export type StringToArray<S extends string, Options extends StringToArrayOptions = {}> = | ||
| IfNotAnyOrNever< | ||
| S, | ||
| { | ||
| ifNot: _StringToArray<S, ApplyDefaultOptions<StringToArrayOptions, DefaultStringToArrayOptions, Options>>; | ||
| ifAny: unknown[]; | ||
| } | ||
| >; | ||
| type _StringToArray<S extends string, Options extends Required<StringToArrayOptions>, Accumulator extends string[] = []> = | ||
| S extends `${infer First}${infer Rest}` | ||
| ? Or<IsStringLiteral<First>, Options['mapNonLiteralsDirectly']> extends true | ||
| ? _StringToArray<Rest, Options, [...Accumulator, First]> | ||
| : _StringToArray<Rest, Options, [...Accumulator, ...First[]]> | ||
| : S extends '' | ||
| ? Accumulator | ||
| : Options['mapNonLiteralsDirectly'] extends true | ||
| ? [...Accumulator, S] | ||
| : [...Accumulator, ...S[]]; | ||
| export {}; |
| import type {IfNotAnyOrNever} from './internal/type.d.ts'; | ||
| import type {NegativeInfinity, PositiveInfinity} from './numeric.d.ts'; | ||
| /** | ||
| Converts a numeric string to a number. | ||
| @example | ||
| ``` | ||
| import type {StringToNumber} from 'type-fest'; | ||
| type PositiveInteger = StringToNumber<'1234'>; | ||
| //=> 1234 | ||
| type NegativeInteger = StringToNumber<'-1234'>; | ||
| //=> -1234 | ||
| type PositiveFloat = StringToNumber<'1234.56'>; | ||
| //=> 1234.56 | ||
| type NegativeFloat = StringToNumber<'-1234.56'>; | ||
| //=> -1234.56 | ||
| type PositiveInfinity = StringToNumber<'Infinity'>; | ||
| //=> Infinity | ||
| type NegativeInfinity = StringToNumber<'-Infinity'>; | ||
| //=> -Infinity | ||
| ``` | ||
| Note: Some strings, such as `'1.50'`, `'0b10'`, or `'12_345'`, may look like they can be converted to numbers, but they don't actually have corresponding numeric literals. So, in such cases, this type produces `never`. See [type-fest#1446](https://github.com/sindresorhus/type-fest/pull/1446) for more details. | ||
| @example | ||
| ``` | ||
| import type {StringToNumber} from 'type-fest'; | ||
| type FractionalsEndingInZero = StringToNumber<'1.50'>; | ||
| //=> never | ||
| type NonDecimalBases = StringToNumber<'0b10' | '0o10' | '0x10'>; | ||
| //=> never | ||
| type NumericSeparators = StringToNumber<'12_345'>; | ||
| //=> never | ||
| ``` | ||
| @category String | ||
| @category Numeric | ||
| @category Template literal | ||
| */ | ||
| export type StringToNumber<S extends string> = IfNotAnyOrNever<S, {ifNot: _StringToNumber<S>; ifAny: number}>; | ||
| type _StringToNumber<S extends string> = | ||
| S extends `${infer N extends number}` | ||
| ? number extends N | ||
| ? `${number}` extends S | ||
| ? N | ||
| : never | ||
| : N | ||
| : string extends S | ||
| ? number | ||
| : S extends 'Infinity' | ||
| ? PositiveInfinity | ||
| : S extends '-Infinity' | ||
| ? NegativeInfinity | ||
| : never; | ||
| export {}; |
+4
-0
@@ -180,2 +180,3 @@ // Basic | ||
| export type {UnionLength} from './source/union-length.d.ts'; | ||
| export type {StringToNumber} from './source/string-to-number.d.ts'; | ||
@@ -212,2 +213,4 @@ // Template literal types | ||
| export type {RemoveSuffix, RemoveSuffixOptions} from './source/remove-suffix.d.ts'; | ||
| export type {StringToArray, StringToArrayOptions} from './source/string-to-array.d.ts'; | ||
| export type {StringLength} from './source/string-length.d.ts'; | ||
@@ -224,3 +227,4 @@ // Miscellaneous | ||
| export type {ExcludeExactly} from './source/exclude-exactly.d.ts'; | ||
| export type {ExtractExactly} from './source/extract-exactly.d.ts'; | ||
| export {}; |
+1
-1
| { | ||
| "name": "type-fest", | ||
| "version": "5.7.0", | ||
| "version": "5.8.0", | ||
| "description": "A collection of essential TypeScript types", | ||
@@ -5,0 +5,0 @@ "license": "(MIT OR CC0-1.0)", |
+4
-0
@@ -268,2 +268,4 @@ <div align="center"> | ||
| - [`RemoveSuffix`](source/remove-suffix.d.ts) - Remove the specified suffix from the end of a string. | ||
| - [`StringToArray`](source/string-to-array.d.ts) - Returns an array of the characters of the specified string. | ||
| - [`StringLength`](source/string-length.d.ts) - Returns the length of the given string. | ||
@@ -314,2 +316,3 @@ ### Array | ||
| - [`Absolute`](source/absolute.d.ts) - Returns the absolute value of the specified number or bigint. | ||
| - [`StringToNumber`](source/string-to-number.d.ts) - Converts a numeric string to a number. | ||
@@ -347,2 +350,3 @@ ### Change case | ||
| - [`ExcludeExactly`](source/exclude-exactly.d.ts) - A stricter version of `Exclude<T, U>` that excludes types only when they are exactly identical. | ||
| - [`ExtractExactly`](source/extract-exactly.d.ts) - A stricter version of `Extract<T, U>` that extracts types only when they are exactly identical. | ||
@@ -349,0 +353,0 @@ ## Declined types |
@@ -1,2 +0,2 @@ | ||
| import type {StringToNumber} from './internal/string.d.ts'; | ||
| import type {StringToNumber} from './string-to-number.d.ts'; | ||
@@ -3,0 +3,0 @@ /** |
@@ -106,4 +106,4 @@ import type {CollapseRestElement} from './internal/array.d.ts'; | ||
| type _AllExtend<TArray extends UnknownArray, Type, Options extends Required<AllExtendOptions>> = IfNotAnyOrNever<TArray, | ||
| TArray extends readonly [infer First, ...infer Rest] | ||
| type _AllExtend<TArray extends UnknownArray, Type, Options extends Required<AllExtendOptions>> = IfNotAnyOrNever<TArray, { | ||
| ifNot: TArray extends readonly [infer First, ...infer Rest] | ||
| ? IsNever<First> extends true | ||
@@ -117,5 +117,7 @@ ? Or<Or<IsNever<Type>, IsAny<Type>>, Not<Options['strictNever']>> extends true | ||
| : false | ||
| : true, | ||
| false, false>; | ||
| : true; | ||
| ifAny: false; | ||
| ifNever: false; | ||
| }>; | ||
| export {}; |
@@ -54,8 +54,9 @@ import type {If} from './if.d.ts'; | ||
| */ | ||
| export type ArrayReverse<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, | ||
| TArray extends unknown // For distributing `TArray` | ||
| export type ArrayReverse<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, { | ||
| ifNot: TArray extends unknown // For distributing `TArray` | ||
| ? _ArrayReverse<TArray> extends infer Result | ||
| ? If<IsArrayReadonly<TArray>, Readonly<Result>, Result> | ||
| : never // Should never happen | ||
| : never>; // Should never happen | ||
| : never; // Should never happen | ||
| }>; | ||
@@ -62,0 +63,0 @@ type _ArrayReverse< |
@@ -54,9 +54,9 @@ import type {If} from './if.d.ts'; | ||
| */ | ||
| export type ArrayTail<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, | ||
| TArray extends UnknownArray // For distributing `TArray` | ||
| export type ArrayTail<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, { | ||
| ifNot: TArray extends UnknownArray // For distributing `TArray` | ||
| ? _ArrayTail<TArray> extends infer Result | ||
| ? If<IsArrayReadonly<TArray>, Readonly<Result>, Result> | ||
| : never // Should never happen | ||
| : never | ||
| >; | ||
| : never; | ||
| }>; | ||
@@ -63,0 +63,0 @@ type _ArrayTail<TArray extends UnknownArray> = TArray extends readonly [unknown?, ...infer Tail] |
@@ -53,3 +53,3 @@ import type {ExtendsStrict} from './extends-strict.d.ts'; | ||
| export type ConditionalKeys<Base, Condition> = (Base extends UnknownArray ? TupleToObject<Base> : Base) extends infer _Base // Remove non-numeric keys from arrays | ||
| ? IfNotAnyOrNever<_Base, _ConditionalKeys<_Base, Condition>, keyof _Base> | ||
| ? IfNotAnyOrNever<_Base, {ifNot: _ConditionalKeys<_Base, Condition>; ifAny: keyof _Base}> | ||
| : never; | ||
@@ -56,0 +56,0 @@ |
@@ -36,23 +36,23 @@ import type {IsNever} from './is-never.d.ts'; | ||
| export type ExcludeExactly<Union, Delete> = | ||
| IfNotAnyOrNever< | ||
| Union, | ||
| _ExcludeExactly<Union, Delete>, | ||
| IfNotAnyOrNever<Union, { | ||
| ifNot: _ExcludeExactly<Union, Delete>; | ||
| // If `Union` is `any`, then if `Delete` is `any`, return `never`, else return `Union`. | ||
| If<IsAny<Delete>, never, Union>, | ||
| ifAny: If<IsAny<Delete>, never, Union>; | ||
| // If `Union` is `never`, then if `Delete` is `never`, return `never`, else return `Union`. | ||
| If<IsNever<Delete>, never, Union> | ||
| >; | ||
| ifNever: If<IsNever<Delete>, never, Union>; | ||
| }>; | ||
| type _ExcludeExactly<Union, Delete> = | ||
| IfNotAnyOrNever<Delete, | ||
| Union extends unknown // For distributing `Union` | ||
| IfNotAnyOrNever<Delete, { | ||
| ifNot: Union extends unknown // For distributing `Union` | ||
| ? [Delete extends unknown // For distributing `Delete` | ||
| ? If<IsEqual<Union, Delete>, true, never> | ||
| : never] extends [never] ? Union : never | ||
| : never, | ||
| : never; | ||
| // If `Delete` is `any` or `never`, then return `Union`, | ||
| // because `Union` cannot be `any` or `never` here. | ||
| Union, Union | ||
| >; | ||
| ifAny: Union; | ||
| ifNever: Union; | ||
| }>; | ||
| export {}; |
@@ -30,4 +30,4 @@ import type {SplitOnRestElement} from './split-on-rest-element.d.ts'; | ||
| */ | ||
| export type ExcludeRestElement<Array_ extends UnknownArray> = IfNotAnyOrNever<Array_, | ||
| SplitOnRestElement<Array_> extends infer Result | ||
| export type ExcludeRestElement<Array_ extends UnknownArray> = IfNotAnyOrNever<Array_, { | ||
| ifNot: SplitOnRestElement<Array_> extends infer Result | ||
| ? Result extends readonly UnknownArray[] | ||
@@ -38,5 +38,5 @@ ? IsArrayReadonly<Array_> extends true | ||
| : never | ||
| : never | ||
| >; | ||
| : never; | ||
| }>; | ||
| export {}; |
@@ -128,9 +128,9 @@ import type {If} from './if.d.ts'; | ||
| */ | ||
| export type ExclusifyUnion<Union> = IfNotAnyOrNever<Union, | ||
| If<IsUnknown<Union>, Union, | ||
| export type ExclusifyUnion<Union> = IfNotAnyOrNever<Union, { | ||
| ifNot: If<IsUnknown<Union>, Union, | ||
| Extract<Union, NonRecursiveType | MapsSetsOrArrays> extends infer SkippedMembers | ||
| ? SkippedMembers | _ExclusifyUnion<Exclude<Union, SkippedMembers>> | ||
| : never | ||
| > | ||
| >; | ||
| >; | ||
| }>; | ||
@@ -137,0 +137,0 @@ type _ExclusifyUnion<Union, UnionCopy = Union> = Union extends unknown // For distributing `Union` |
@@ -114,3 +114,3 @@ import type {If} from '../if.d.ts'; | ||
| */ | ||
| export type CollapseRestElement<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, _CollapseRestElement<TArray>>; | ||
| export type CollapseRestElement<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, {ifNot: _CollapseRestElement<TArray>}>; | ||
@@ -117,0 +117,0 @@ type _CollapseRestElement< |
@@ -29,7 +29,9 @@ import type {IsNever} from '../is-never.d.ts'; | ||
| export type IsNumberLike<N> = | ||
| IfNotAnyOrNever<N, | ||
| N extends number | `${number}` | ||
| IfNotAnyOrNever<N, { | ||
| ifNot: N extends number | `${number}` | ||
| ? true | ||
| : false, | ||
| boolean, false>; | ||
| : false; | ||
| ifAny: boolean; | ||
| ifNever: false; | ||
| }>; | ||
@@ -36,0 +38,0 @@ /** |
@@ -9,5 +9,6 @@ import type {Simplify} from '../simplify.d.ts'; | ||
| import type {IsNever} from '../is-never.d.ts'; | ||
| import type {StringToNumber} from '../string-to-number.d.ts'; | ||
| import type {FilterDefinedKeys, FilterOptionalKeys} from './keys.d.ts'; | ||
| import type {MapsSetsOrArrays, NonRecursiveType} from './type.d.ts'; | ||
| import type {StringToNumber, ToString} from './string.d.ts'; | ||
| import type {ToString} from './string.d.ts'; | ||
@@ -14,0 +15,0 @@ /** |
| import type {TupleOf} from '../tuple-of.d.ts'; | ||
| import type {NegativeInfinity, PositiveInfinity} from '../numeric.d.ts'; | ||
| import type {Trim} from '../trim.d.ts'; | ||
| import type {StringLength} from '../string-length.d.ts'; | ||
| import type {Whitespace} from './characters.d.ts'; | ||
@@ -14,38 +14,2 @@ | ||
| /** | ||
| Converts a numeric string to a number. | ||
| @example | ||
| ``` | ||
| type PositiveInt = StringToNumber<'1234'>; | ||
| //=> 1234 | ||
| type NegativeInt = StringToNumber<'-1234'>; | ||
| //=> -1234 | ||
| type PositiveFloat = StringToNumber<'1234.56'>; | ||
| //=> 1234.56 | ||
| type NegativeFloat = StringToNumber<'-1234.56'>; | ||
| //=> -1234.56 | ||
| type PositiveInfinity = StringToNumber<'Infinity'>; | ||
| //=> Infinity | ||
| type NegativeInfinity = StringToNumber<'-Infinity'>; | ||
| //=> -Infinity | ||
| ``` | ||
| @category String | ||
| @category Numeric | ||
| @category Template literal | ||
| */ | ||
| export type StringToNumber<S extends string> = S extends `${infer N extends number}` | ||
| ? N | ||
| : S extends 'Infinity' | ||
| ? PositiveInfinity | ||
| : S extends '-Infinity' | ||
| ? NegativeInfinity | ||
| : never; | ||
| /** | ||
| Returns a boolean for whether the given string `S` starts with the given string `SearchString`. | ||
@@ -78,41 +42,2 @@ | ||
| /** | ||
| Returns an array of the characters of the string. | ||
| @example | ||
| ``` | ||
| type A = StringToArray<'abcde'>; | ||
| //=> ['a', 'b', 'c', 'd', 'e'] | ||
| type B = StringToArray<string>; | ||
| //=> never | ||
| ``` | ||
| @category String | ||
| */ | ||
| export type StringToArray<S extends string, Result extends string[] = []> = string extends S | ||
| ? never | ||
| : S extends `${infer F}${infer R}` | ||
| ? StringToArray<R, [...Result, F]> | ||
| : Result; | ||
| /** | ||
| Returns the length of the given string. | ||
| @example | ||
| ``` | ||
| type A = StringLength<'abcde'>; | ||
| //=> 5 | ||
| type B = StringLength<string>; | ||
| //=> never | ||
| ``` | ||
| @category String | ||
| @category Template literal | ||
| */ | ||
| export type StringLength<S extends string> = string extends S | ||
| ? never | ||
| : StringToArray<S>['length']; | ||
| /** | ||
| Returns a boolean for whether a string is whitespace. | ||
@@ -119,0 +44,0 @@ */ |
@@ -1,2 +0,1 @@ | ||
| import type {If} from '../if.d.ts'; | ||
| import type {IsAny} from '../is-any.d.ts'; | ||
@@ -6,3 +5,2 @@ import type {IsNever} from '../is-never.d.ts'; | ||
| import type {UnknownArray} from '../unknown-array.d.ts'; | ||
| import type {UnionToIntersection} from '../union-to-intersection.d.ts'; | ||
@@ -95,12 +93,12 @@ /** | ||
| ``` | ||
| // When `T` is a NOT `any` or `never` (like `string`) => Returns `IfNotAnyOrNever` branch | ||
| type A = IfNotAnyOrNever<string, 'VALID', 'IS_ANY', 'IS_NEVER'>; | ||
| // When `T` is neither `any` nor `never` (like `string`) => Returns `IfNot` branch | ||
| type A = IfNotAnyOrNever<string, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>; | ||
| //=> 'VALID' | ||
| // When `T` is `any` => Returns `IfAny` branch | ||
| type B = IfNotAnyOrNever<any, 'VALID', 'IS_ANY', 'IS_NEVER'>; | ||
| type B = IfNotAnyOrNever<any, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>; | ||
| //=> 'IS_ANY' | ||
| // When `T` is `never` => Returns `IfNever` branch | ||
| type C = IfNotAnyOrNever<never, 'VALID', 'IS_ANY', 'IS_NEVER'>; | ||
| type C = IfNotAnyOrNever<never, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>; | ||
| //=> 'IS_NEVER' | ||
@@ -118,3 +116,3 @@ ``` | ||
| // The following implementation is not tail recursive | ||
| type TrimLeft<S extends string> = IfNotAnyOrNever<S, S extends ` ${infer R}` ? TrimLeft<R> : S>; | ||
| type TrimLeft<S extends string> = IfNotAnyOrNever<S, {ifNot: S extends ` ${infer R}` ? TrimLeft<R> : S}>; | ||
@@ -128,3 +126,3 @@ // Hence, instantiations with long strings will fail | ||
| // To fix this, move the recursion into a helper type | ||
| type TrimLeftOptimised<S extends string> = IfNotAnyOrNever<S, _TrimLeftOptimised<S>>; | ||
| type TrimLeftOptimised<S extends string> = IfNotAnyOrNever<S, {ifNot: _TrimLeftOptimised<S>}>; | ||
@@ -137,4 +135,12 @@ type _TrimLeftOptimised<S extends string> = S extends ` ${infer R}` ? _TrimLeftOptimised<R> : S; | ||
| */ | ||
| export type IfNotAnyOrNever<T, IfNotAnyOrNever, IfAny = any, IfNever = never> = | ||
| If<IsAny<T>, IfAny, If<IsNever<T>, IfNever, IfNotAnyOrNever>>; | ||
| export type IfNotAnyOrNever<T, Cases extends {ifNot: unknown; ifAny?: unknown; ifNever?: unknown}> = | ||
| IsAny<T> extends true | ||
| ? 'ifAny' extends keyof Cases | ||
| ? Cases['ifAny'] | ||
| : any | ||
| : IsNever<T> extends true | ||
| ? 'ifNever' extends keyof Cases | ||
| ? Cases['ifNever'] | ||
| : never | ||
| : Cases['ifNot']; | ||
@@ -141,0 +147,0 @@ /** |
@@ -117,5 +117,7 @@ import type {Primitive} from './primitive.d.ts'; | ||
| */ | ||
| export type IsStringLiteral<S> = IfNotAnyOrNever<S, | ||
| _IsStringLiteral<CollapseLiterals<S extends TagContainer<any> ? UnwrapTagged<S> : S>>, | ||
| false, false>; | ||
| export type IsStringLiteral<S> = IfNotAnyOrNever<S, { | ||
| ifNot: _IsStringLiteral<CollapseLiterals<S extends TagContainer<any> ? UnwrapTagged<S> : S>>; | ||
| ifAny: false; | ||
| ifNever: false; | ||
| }>; | ||
@@ -122,0 +124,0 @@ export type _IsStringLiteral<S> = |
@@ -0,1 +1,6 @@ | ||
| import type {If} from './if.d.ts'; | ||
| import type {IfNotAnyOrNever, IsExactOptionalPropertyTypesEnabled} from './internal/type.d.ts'; | ||
| import type {SplitOnRestElement} from './split-on-rest-element.d.ts'; | ||
| import type {UnknownArray} from './unknown-array.d.ts'; | ||
| /** | ||
@@ -19,19 +24,67 @@ Extract the type of the last element of an array. | ||
| Note: When the array ends with an optional or rest element, the last element's position becomes ambiguous. In such cases, the result is a union of the types of all elements that could potentially be the last element of the array. | ||
| @example | ||
| ``` | ||
| import type {LastArrayElement} from 'type-fest'; | ||
| type A = LastArrayElement<[string, number?, bigint?]>; | ||
| //=> bigint | number | string | ||
| type B = LastArrayElement<[string, number, bigint?, ...boolean[]]>; | ||
| //=> boolean | bigint | number | ||
| ``` | ||
| Note: If empty array is a valid value for the array type, the result includes an `undefined`. This aligns with the runtime behavior of `[].at(-1)`. | ||
| @example | ||
| ``` | ||
| import type {LastArrayElement} from 'type-fest'; | ||
| type A = LastArrayElement<[]>; | ||
| //=> undefined | ||
| // `[]` is assignable to `string[]` | ||
| type B = LastArrayElement<string[]>; | ||
| //=> string | undefined | ||
| // `[]` is assignable to `[string?, number?]` | ||
| type C = LastArrayElement<[string?, number?]>; | ||
| //=> number | string | undefined | ||
| // `[]` is assignable to [string?, number?, ...bigint[]]` | ||
| type D = LastArrayElement<[string?, number?, ...bigint[]]>; | ||
| //=> bigint | number | string | undefined | ||
| ``` | ||
| @category Array | ||
| @category Template literal | ||
| */ | ||
| export type LastArrayElement<Elements extends readonly unknown[], ElementBeforeTailingSpreadElement = never> = | ||
| // If the last element of an array is a spread element, the `LastArrayElement` result should be `'the type of the element before the spread element' | 'the type of the spread element'`. | ||
| Elements extends readonly [] | ||
| ? ElementBeforeTailingSpreadElement | ||
| : Elements extends readonly [...infer U, infer V] | ||
| ? V | ||
| : Elements extends readonly [infer U, ...infer V] | ||
| // If we return `V[number] | U` directly, it would be wrong for `[[string, boolean, object, ...number[]]`. | ||
| // So we need to recurse type `V` and carry over the type of the element before the spread element. | ||
| ? LastArrayElement<V, U> | ||
| : Elements extends ReadonlyArray<infer U> | ||
| ? U | ElementBeforeTailingSpreadElement | ||
| : never; | ||
| export type LastArrayElement<TArray extends UnknownArray> = | ||
| IfNotAnyOrNever<TArray, { | ||
| ifNot: TArray extends UnknownArray // For distributing `TArray` | ||
| ? SplitOnRestElement<TArray> extends readonly [infer BeforeRest extends UnknownArray, infer Rest extends UnknownArray, infer AfterRest extends UnknownArray] | ||
| ? _LastArrayElement<BeforeRest, Rest, AfterRest> | ||
| : never | ||
| : never; | ||
| }>; | ||
| type _LastArrayElement<BeforeRest extends UnknownArray, Rest extends UnknownArray, AfterRest extends UnknownArray> = | ||
| AfterRest extends readonly [...any, infer Last] // Note there are no optional elements in `AfterRest`. | ||
| ? Last // If there's a `Last` in `AfterRest`, then that's the result. | ||
| : Rest[number] | BeforeRestLastElement<BeforeRest>; // Otherwise, the result is union of the `Rest` element and the last element in `BeforeRest`. | ||
| type BeforeRestLastElement<BeforeRest extends UnknownArray, Accumulator = never> = | ||
| BeforeRest extends readonly [] | ||
| ? Accumulator | undefined | ||
| : BeforeRest extends readonly [...any, infer Last] | ||
| ? Last | Accumulator | ||
| : BeforeRest extends readonly [...infer Rest, (infer Last)?] | ||
| ? BeforeRestLastElement< | ||
| Rest, | ||
| // Add `undefined` for optional elements, if `exactOptionalPropertyTypes` is disabled. | ||
| Last | Accumulator | If<IsExactOptionalPropertyTypesEnabled, never, undefined> | ||
| > | ||
| : never; | ||
| export {}; |
+21
-16
@@ -93,18 +93,23 @@ import type {If} from './if.d.ts'; | ||
| export type ObjectMerge<First extends object, Second extends object> = | ||
| IfNotAnyOrNever<First, IfNotAnyOrNever<Second, First extends unknown // For distributing `First` | ||
| ? Second extends unknown // For distributing `Second` | ||
| ? First extends MapsSetsOrArrays | ||
| ? unknown | ||
| : Second extends MapsSetsOrArrays | ||
| ? unknown | ||
| : _ObjectMerge< | ||
| First, | ||
| Second, | ||
| NormalizedLiteralKeys<First>, | ||
| NormalizedLiteralKeys<Second>, | ||
| IsExactOptionalPropertyTypesEnabled extends true ? Required<First> : First, | ||
| IsExactOptionalPropertyTypesEnabled extends true ? Required<Second> : Second | ||
| > | ||
| : never // Should never happen | ||
| : never>, First & Second>; // Should never happen | ||
| IfNotAnyOrNever<First, { | ||
| ifNot: IfNotAnyOrNever<Second, { | ||
| ifNot: First extends unknown // For distributing `First` | ||
| ? Second extends unknown // For distributing `Second` | ||
| ? First extends MapsSetsOrArrays | ||
| ? unknown | ||
| : Second extends MapsSetsOrArrays | ||
| ? unknown | ||
| : _ObjectMerge< | ||
| First, | ||
| Second, | ||
| NormalizedLiteralKeys<First>, | ||
| NormalizedLiteralKeys<Second>, | ||
| IsExactOptionalPropertyTypesEnabled extends true ? Required<First> : First, | ||
| IsExactOptionalPropertyTypesEnabled extends true ? Required<Second> : Second | ||
| > | ||
| : never // Should never happen | ||
| : never; // Should never happen | ||
| }>; | ||
| ifAny: First & Second; | ||
| }>; | ||
@@ -111,0 +116,0 @@ type _ObjectMerge< |
@@ -97,10 +97,9 @@ import type {ApplyDefaultOptions} from './internal/object.d.ts'; | ||
| export type RemovePrefix<S extends string, Prefix extends string, Options extends RemovePrefixOptions = {}> = | ||
| IfNotAnyOrNever< | ||
| S, | ||
| If< | ||
| IfNotAnyOrNever<S, { | ||
| ifNot: If< | ||
| IsNever<Prefix>, | ||
| S, | ||
| _RemovePrefix<S, Prefix, ApplyDefaultOptions<RemovePrefixOptions, DefaultRemovePrefixOptions, Options>> | ||
| > | ||
| >; | ||
| >; | ||
| }>; | ||
@@ -107,0 +106,0 @@ type _RemovePrefix<S extends string, Prefix extends string, Options extends Required<RemovePrefixOptions>> = |
@@ -97,10 +97,9 @@ import type {ApplyDefaultOptions} from './internal/object.d.ts'; | ||
| export type RemoveSuffix<S extends string, Suffix extends string, Options extends RemoveSuffixOptions = {}> = | ||
| IfNotAnyOrNever< | ||
| S, | ||
| If< | ||
| IfNotAnyOrNever<S, { | ||
| ifNot: If< | ||
| IsNever<Suffix>, | ||
| S, | ||
| _RemoveSuffix<S, Suffix, ApplyDefaultOptions<RemoveSuffixOptions, DefaultRemoveSuffixOptions, Options>> | ||
| > | ||
| >; | ||
| >; | ||
| }>; | ||
@@ -107,0 +106,0 @@ type _RemoveSuffix<S extends string, Suffix extends string, Options extends Required<RemoveSuffixOptions>> = |
@@ -43,7 +43,7 @@ import type {If} from './if.d.ts'; | ||
| export type RequireAllOrNone<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> = | ||
| IfNotAnyOrNever<ObjectType, | ||
| If<IsNever<KeysType>, | ||
| IfNotAnyOrNever<ObjectType, { | ||
| ifNot: If<IsNever<KeysType>, | ||
| ObjectType, | ||
| _RequireAllOrNone<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>> | ||
| >>; | ||
| _RequireAllOrNone<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>>>; | ||
| }>; | ||
@@ -50,0 +50,0 @@ type _RequireAllOrNone<ObjectType, KeysType extends keyof ObjectType> = ( |
@@ -32,7 +32,7 @@ import type {Except} from './except.d.ts'; | ||
| > = | ||
| IfNotAnyOrNever<ObjectType, | ||
| If<IsNever<KeysType>, | ||
| IfNotAnyOrNever<ObjectType, { | ||
| ifNot: If<IsNever<KeysType>, | ||
| never, | ||
| _RequireAtLeastOne<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>> | ||
| >>; | ||
| _RequireAtLeastOne<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>>>; | ||
| }>; | ||
@@ -39,0 +39,0 @@ type _RequireAtLeastOne< |
@@ -36,7 +36,7 @@ import type {If} from './if.d.ts'; | ||
| export type RequireExactlyOne<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> = | ||
| IfNotAnyOrNever<ObjectType, | ||
| If<IsNever<KeysType>, | ||
| IfNotAnyOrNever<ObjectType, { | ||
| ifNot: If<IsNever<KeysType>, | ||
| never, | ||
| _RequireExactlyOne<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>> | ||
| >>; | ||
| _RequireExactlyOne<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>>>; | ||
| }>; | ||
@@ -43,0 +43,0 @@ type _RequireExactlyOne<ObjectType, KeysType extends keyof ObjectType> = |
@@ -38,7 +38,7 @@ import type {RequireExactlyOne} from './require-exactly-one.d.ts'; | ||
| export type RequireOneOrNone<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> = | ||
| IfNotAnyOrNever<ObjectType, | ||
| If<IsNever<KeysType>, | ||
| IfNotAnyOrNever<ObjectType, { | ||
| ifNot: If<IsNever<KeysType>, | ||
| ObjectType, | ||
| _RequireOneOrNone<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>> | ||
| >>; | ||
| _RequireOneOrNone<ObjectType, If<IsAny<KeysType>, keyof ObjectType, KeysType>>>; | ||
| }>; | ||
@@ -45,0 +45,0 @@ type _RequireOneOrNone<ObjectType, KeysType extends keyof ObjectType> = ( |
+16
-8
| import type {ApplyDefaultOptions} from './internal/object.d.ts'; | ||
| import type {IfNotAnyOrNever, NonRecursiveType} from './internal/type.d.ts'; | ||
| import type {IsAny} from './is-any.d.ts'; | ||
| import type {IsUnknown} from './is-unknown.d.ts'; | ||
| import type {OptionalKeysOf} from './optional-keys-of.d.ts'; | ||
@@ -94,14 +96,20 @@ import type {Simplify} from './simplify.d.ts'; | ||
| export type Schema<Type, Value, Options extends SchemaOptions = {}> = | ||
| IfNotAnyOrNever<Type, | ||
| _Schema<Type, Value, ApplyDefaultOptions<SchemaOptions, DefaultSchemaOptions, Options>>, | ||
| Value, Value>; | ||
| IfNotAnyOrNever<Type, { | ||
| ifNot: _Schema<Type, Value, ApplyDefaultOptions<SchemaOptions, DefaultSchemaOptions, Options>>; | ||
| ifAny: Value; | ||
| ifNever: Value; | ||
| }>; | ||
| type _Schema<Type, Value, Options extends Required<SchemaOptions>> = | ||
| Type extends NonRecursiveType | Map<unknown, unknown> | Set<unknown> | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | ||
| IsAny<Type> extends true | ||
| ? Value | ||
| : Type extends UnknownArray | ||
| ? Options['recurseIntoArrays'] extends false | ||
| : IsUnknown<Type> extends true | ||
| ? Value | ||
| : Type extends NonRecursiveType | Map<unknown, unknown> | Set<unknown> | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | ||
| ? Value | ||
| : SchemaHelper<Type, Value, Options> | ||
| : SchemaHelper<Type, Value, Options>; | ||
| : Type extends UnknownArray | ||
| ? Options['recurseIntoArrays'] extends false | ||
| ? Value | ||
| : SchemaHelper<Type, Value, Options> | ||
| : SchemaHelper<Type, Value, Options>; | ||
@@ -108,0 +116,0 @@ /** |
@@ -1,2 +0,2 @@ | ||
| import type {NonRecursiveType, StringToNumber} from './internal/index.d.ts'; | ||
| import type {NonRecursiveType} from './internal/index.d.ts'; | ||
| import type {IsAny} from './is-any.d.ts'; | ||
@@ -7,2 +7,3 @@ import type {NonNullableDeep} from './non-nullable-deep.d.ts'; | ||
| import type {Simplify} from './simplify.d.ts'; | ||
| import type {StringToNumber} from './string-to-number.d.ts'; | ||
| import type {UnionToTuple} from './union-to-tuple.d.ts'; | ||
@@ -9,0 +10,0 @@ import type {UnknownArray} from './unknown-array.d.ts'; |
| import type {IsAny} from './is-any.d.ts'; | ||
| import type {NonRecursiveType, StringToNumber} from './internal/index.d.ts'; | ||
| import type {NonRecursiveType} from './internal/index.d.ts'; | ||
| import type {Paths} from './paths.d.ts'; | ||
@@ -9,2 +9,3 @@ import type {SetRequired} from './set-required.d.ts'; | ||
| import type {UnknownArray} from './unknown-array.d.ts'; | ||
| import type {StringToNumber} from './string-to-number.d.ts'; | ||
@@ -11,0 +12,0 @@ /** |
@@ -100,4 +100,4 @@ import type {CollapseRestElement} from './internal/array.d.ts'; | ||
| type _SomeExtend<TArray extends UnknownArray, Type, Options extends Required<SomeExtendOptions>> = IfNotAnyOrNever<TArray, | ||
| TArray extends readonly [infer First, ...infer Rest] | ||
| type _SomeExtend<TArray extends UnknownArray, Type, Options extends Required<SomeExtendOptions>> = IfNotAnyOrNever<TArray, { | ||
| ifNot: TArray extends readonly [infer First, ...infer Rest] | ||
| ? IsNever<First> extends true | ||
@@ -111,5 +111,7 @@ ? Or<Or<IsNever<Type>, IsAny<Type>>, Not<Options['strictNever']>> extends true | ||
| : _SomeExtend<Rest, Type, Options> | ||
| : false, | ||
| false, false>; | ||
| : false; | ||
| ifAny: false; | ||
| ifNever: false; | ||
| }>; | ||
| export {}; |
@@ -68,6 +68,8 @@ import type {IfNotAnyOrNever, IsExactOptionalPropertyTypesEnabled} from './internal/type.d.ts'; | ||
| Array_ extends unknown // For distributing `Array_` | ||
| ? IfNotAnyOrNever<Array_, _SplitOnRestElement< | ||
| Array_, | ||
| ApplyDefaultOptions<SplitOnRestElementOptions, DefaultSplitOnRestElementOptions, Options> | ||
| >> extends infer Result extends UnknownArray | ||
| ? IfNotAnyOrNever<Array_, { | ||
| ifNot: _SplitOnRestElement< | ||
| Array_, | ||
| ApplyDefaultOptions<SplitOnRestElementOptions, DefaultSplitOnRestElementOptions, Options> | ||
| >; | ||
| }> extends infer Result extends UnknownArray | ||
| ? If<IsArrayReadonly<Array_>, Readonly<Result>, Result> | ||
@@ -74,0 +76,0 @@ : never // Should never happen |
| import type {IsNumericLiteral} from './is-literal.d.ts'; | ||
| import type {IsNegative} from './numeric.d.ts'; | ||
| import type {DigitCharacter} from './characters.d.ts'; | ||
@@ -12,5 +13,5 @@ /** | ||
| declare function stringRepeat< | ||
| Input extends string, | ||
| S extends string, | ||
| Count extends number, | ||
| >(input: Input, count: Count): StringRepeat<Input, Count>; | ||
| >(input: S, count: Count): StringRepeat<S, Count>; | ||
@@ -26,26 +27,52 @@ // The return type is the exact string literal, not just `string`. | ||
| Note: If the specified count has a decimal part, the decimal part will be ignored. | ||
| @example | ||
| ``` | ||
| import type {StringRepeat} from 'type-fest'; | ||
| type DecimalCount = StringRepeat<'foo', 2.5>; | ||
| //=> 'foofoo' | ||
| ``` | ||
| @category String | ||
| @category Template literal | ||
| */ | ||
| export type StringRepeat< | ||
| Input extends string, | ||
| Count extends number, | ||
| > = StringRepeatHelper<Input, Count>; | ||
| type StringRepeatHelper< | ||
| Input extends string, | ||
| Count extends number, | ||
| Counter extends never[] = [], | ||
| Accumulator extends string = '', | ||
| > = | ||
| IsNegative<Count> extends true | ||
| ? never | ||
| : Input extends '' | ||
| ? '' | ||
| : Count extends Counter['length'] | ||
| ? Accumulator | ||
| export type StringRepeat<S extends string, Count extends number> = | ||
| Count extends unknown // To distribute `Count` | ||
| ? IsNegative<Count> extends true | ||
| ? never | ||
| : S extends '' | ||
| ? '' | ||
| : IsNumericLiteral<Count> extends false | ||
| ? string | ||
| : StringRepeatHelper<Input, Count, [...Counter, never], `${Accumulator}${Input}`>; | ||
| : `${Count}` extends `${string}e${string}` | ||
| ? string | ||
| : BuildStringDigitByDigit<S, `${Count}`> | ||
| : never; | ||
| type BuildStringDigitByDigit<S extends string, Count extends string, Accumulator extends string = ''> = | ||
| Count extends `${infer First extends DigitCharacter}${infer Rest}` | ||
| ? BuildStringDigitByDigit< | ||
| S, | ||
| Rest, | ||
| `${RepeatStringTenTimes<Accumulator>}${DigitStringRepeat<S, First>}` | ||
| > | ||
| : Accumulator; | ||
| type RepeatStringTenTimes<S extends string> = `${S}${S}${S}${S}${S}${S}${S}${S}${S}${S}`; | ||
| type DigitStringRepeat<S extends string, Digit extends DigitCharacter> = [ | ||
| '', | ||
| `${S}`, | ||
| `${S}${S}`, | ||
| `${S}${S}${S}`, | ||
| `${S}${S}${S}${S}`, | ||
| `${S}${S}${S}${S}${S}`, | ||
| `${S}${S}${S}${S}${S}${S}`, | ||
| `${S}${S}${S}${S}${S}${S}${S}`, | ||
| `${S}${S}${S}${S}${S}${S}${S}${S}`, | ||
| `${S}${S}${S}${S}${S}${S}${S}${S}${S}`, | ||
| ][Digit]; | ||
| export {}; |
| import type {Join} from './join.d.ts'; | ||
| import type {ArraySlice} from './array-slice.d.ts'; | ||
| import type {StringToArray} from './internal/index.d.ts'; | ||
| import type {StringToArray} from './string-to-array.d.ts'; | ||
@@ -5,0 +5,0 @@ /** |
@@ -81,5 +81,7 @@ import type {If} from './if.d.ts'; | ||
| */ | ||
| export type TupleOf<Length extends number, Fill = unknown> = IfNotAnyOrNever<Length, | ||
| _TupleOf<If<IsNegative<Length>, 0, Length>, Fill>, | ||
| Fill[], []>; | ||
| export type TupleOf<Length extends number, Fill = unknown> = IfNotAnyOrNever<Length, { | ||
| ifNot: _TupleOf<If<IsNegative<Length>, 0, Length>, Fill>; | ||
| ifAny: Fill[]; | ||
| ifNever: []; | ||
| }>; | ||
@@ -86,0 +88,0 @@ type _TupleOf<Length extends number, Fill> = number extends Length |
569039
1.82%216
1.89%13848
1.75%1086
0.37%