Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions source/all-extend.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ type B = AllExtend<[1?, 2?, 3?], number | undefined>;
export type AllExtend<TArray extends UnknownArray, Type, Options extends AllExtendOptions = {}> =
_AllExtend<CollapseRestElement<TArray>, Type, ApplyDefaultOptions<AllExtendOptions, DefaultAllExtendOptions, Options>>;

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
? Or<Or<IsNever<Type>, IsAny<Type>>, Not<Options['strictNever']>> extends true
// If target `Type` is also `never`, or is `any`, or `strictNever` is disabled, recurse further.
Expand All @@ -113,7 +113,9 @@ type _AllExtend<TArray extends UnknownArray, Type, Options extends Required<AllE
: First extends Type
? _AllExtend<Rest, Type, Options>
: false
: true,
false, false>;
: true;
ifAny: false;
ifNever: false;
}>;

export {};
7 changes: 4 additions & 3 deletions source/array-reverse.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ type E = ArrayReverse<[string?, number?, ...boolean[]]>;

@category Array
*/
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
}>;

type _ArrayReverse<
TArray extends UnknownArray,
Expand Down
8 changes: 4 additions & 4 deletions source/array-tail.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ const availableTopSciFi = curry(searchBooks)('sci-fi')(4.5)(true);

@category Array
*/
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;
}>;

type _ArrayTail<TArray extends UnknownArray> = TArray extends readonly [unknown?, ...infer Tail]
? keyof TArray & `${number}` extends never
Expand Down
2 changes: 1 addition & 1 deletion source/conditional-keys.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type NumberValueIndices = ConditionalKeys<[string, number?, string?], number | u
@category Object
*/
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;

type _ConditionalKeys<Base, Condition> = keyof {
Expand Down
22 changes: 11 additions & 11 deletions source/exclude-exactly.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,25 @@ type TestExcludeExactly3 = ExcludeExactly<{a: string} | {a: string; b: string},
@category Improved Built-in
*/
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 {};
8 changes: 4 additions & 4 deletions source/exclude-rest-element.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ type T4 = ExcludeRestElement<[number, string]>;
@see {@link SplitOnRestElement}
@category Array
*/
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[]
? IsArrayReadonly<Array_> extends true
? Readonly<[...Result[0], ...Result[2]]>
: [...Result[0], ...Result[2]]
: never
: never
>;
: never;
}>;

export {};
8 changes: 4 additions & 4 deletions source/exclusify-union.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,13 @@ type D = ExclusifyUnion<{a?: 1; readonly b: 2} | {d: 4}>;
@category Object
@category Union
*/
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
>
>;
>;
}>;

type _ExclusifyUnion<Union, UnionCopy = Union> = Union extends unknown // For distributing `Union`
? Simplify<
Expand Down
22 changes: 11 additions & 11 deletions source/extract-exactly.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,25 @@ type TestExtractExactly3 = ExtractExactly<{a: string} | {a: string; b: string},
@category Improved Built-in
*/
export type ExtractExactly<Union, Match> =
IfNotAnyOrNever<
Union,
_ExtractExactly<Union, Match>,
IfNotAnyOrNever<Union, {
ifNot: _ExtractExactly<Union, Match>;
// If `Union` is `any`, then if `Match` is `any`, return `any`, else return `never`.
If<IsAny<Match>, Union, never>,
ifAny: If<IsAny<Match>, Union, never>;
// If `Union` is `never`, return `never`, doesn't matter what `Match` is.
never
>;
ifNever: never;
}>;

type _ExtractExactly<Union, Match> =
IfNotAnyOrNever<Match,
Union extends unknown // For distributing `Union`
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,
: never;
// If `Match` is `any` or `never`, then return `never`,
// because `Union` cannot be `any` or `never` here.
never, never
>;
ifAny: never;
ifNever: never;
}>;

export {};
2 changes: 1 addition & 1 deletion source/internal/array.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ type B = CollapseRestElement<[string?, string?, ...number[]]>;
//=> [string | undefined, string | undefined, number]
```
*/
export type CollapseRestElement<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, _CollapseRestElement<TArray>>;
export type CollapseRestElement<TArray extends UnknownArray> = IfNotAnyOrNever<TArray, {ifNot: _CollapseRestElement<TArray>}>;

type _CollapseRestElement<
TArray extends UnknownArray,
Expand Down
10 changes: 6 additions & 4 deletions source/internal/numeric.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ type E = IsNumberLike<'a'>;
//=> false
*/
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;
}>;

/**
Returns the minimum number in the given union of numbers.
Expand Down
26 changes: 16 additions & 10 deletions source/internal/type.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type {If} from '../if.d.ts';
import type {IsAny} from '../is-any.d.ts';
import type {IsNever} from '../is-never.d.ts';
import type {Primitive} from '../primitive.d.ts';
import type {UnknownArray} from '../unknown-array.d.ts';
import type {UnionToIntersection} from '../union-to-intersection.d.ts';

/**
Matches any primitive, `void`, `Date`, or `RegExp` value.
Expand Down Expand Up @@ -91,16 +89,16 @@ An if-else-like type that resolves depending on whether the given type is `any`

@example
```
// 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'}>;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Umm... would this be better?

Suggested change
type A = IfNotAnyOrNever<string, {ifNot: 'VALID'; ifAny: 'IS_ANY'; ifNever: 'IS_NEVER'}>;
type A = IfNotAnyOrNever<string, {neither: 'VALID'; any: 'IS_ANY'; never: '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'
```

Expand All @@ -113,7 +111,7 @@ import type {StringRepeat} from 'type-fest';
type NineHundredNinetyNineSpaces = StringRepeat<' ', 999>;

// 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}>;

// Hence, instantiations with long strings will fail
// @ts-expect-error
Expand All @@ -122,16 +120,24 @@ type T1 = TrimLeft<NineHundredNinetyNineSpaces>;
// Error: Type instantiation is excessively deep and possibly infinite.

// 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>}>;

type _TrimLeftOptimised<S extends string> = S extends ` ${infer R}` ? _TrimLeftOptimised<R> : S;

type T2 = TrimLeftOptimised<NineHundredNinetyNineSpaces>;
//=> ''
```
*/
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'];

/**
Returns a boolean for whether the given type is `any` or `never`.
Expand Down
8 changes: 5 additions & 3 deletions source/is-literal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,11 @@ type L2 = Length<`${number}`>;
@category Type Guard
@category Utilities
*/
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;
}>;

export type _IsStringLiteral<S> =
// If `T` is an infinite string type (e.g., `on${string}`), `Record<T, never>` produces an index signature,
Expand Down
37 changes: 21 additions & 16 deletions source/object-merge.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,27 @@ Note: If you want a simple merge where properties from the second object always
@category Object
*/
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;
}>;

type _ObjectMerge<
First extends object,
Expand Down
9 changes: 4 additions & 5 deletions source/remove-prefix.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,13 @@ type D = RemovePrefix<`handle${Capitalize<string>}`, 'handle'>;
@category Template literal
*/
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>>
>
>;
>;
}>;

type _RemovePrefix<S extends string, Prefix extends string, Options extends Required<RemovePrefixOptions>> =
Prefix extends string // For distributing `Prefix`
Expand Down
9 changes: 4 additions & 5 deletions source/remove-suffix.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,13 @@ type D = RemoveSuffix<`api/${string}/analytics`, '/analytics'>;
@category Template literal
*/
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>>
>
>;
>;
}>;

type _RemoveSuffix<S extends string, Suffix extends string, Options extends Required<RemoveSuffixOptions>> =
Suffix extends string // For distributing `Suffix`
Expand Down
8 changes: 4 additions & 4 deletions source/require-all-or-none.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ const responder2: RequireAllOrNone<Responder, 'text' | 'json'> = {
@category Object
*/
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>>>;
}>;

type _RequireAllOrNone<ObjectType, KeysType extends keyof ObjectType> = (
| RequireAll<ObjectType, KeysType>
Expand Down
Loading