mirror of
https://github.com/sindresorhus/type-fest.git
synced 2026-02-01 15:59:43 +00:00
ArrayTail: Fix behaviour with non-tuple arrays (#1175)
This commit is contained in:
parent
b34b1d865b
commit
f3aabd8a5d
11
source/array-tail.d.ts
vendored
11
source/array-tail.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
import type {If} from './if.d.ts';
|
||||
import type {IsArrayReadonly} from './internal/index.d.ts';
|
||||
import type {IfNotAnyOrNever, IsArrayReadonly} from './internal/index.d.ts';
|
||||
import type {UnknownArray} from './unknown-array.d.ts';
|
||||
|
||||
/**
|
||||
@ -24,15 +24,18 @@ add3(4);
|
||||
|
||||
@category Array
|
||||
*/
|
||||
export type ArrayTail<TArray extends UnknownArray> =
|
||||
export type ArrayTail<TArray extends UnknownArray> = IfNotAnyOrNever<TArray,
|
||||
TArray extends UnknownArray // For distributing `TArray`
|
||||
? _ArrayTail<TArray> extends infer Result
|
||||
? If<IsArrayReadonly<TArray>, Readonly<Result>, Result>
|
||||
: never // Should never happen
|
||||
: never; // Should never happen
|
||||
: never
|
||||
>;
|
||||
|
||||
type _ArrayTail<TArray extends UnknownArray> = TArray extends readonly [unknown?, ...infer Tail]
|
||||
? keyof TArray & `${number}` extends never
|
||||
? []
|
||||
? TArray extends readonly []
|
||||
? []
|
||||
: TArray // Happens when `TArray` is a non-tuple array (e.g., `string[]`) or has a leading rest element (e.g., `[...string[], number]`)
|
||||
: Tail
|
||||
: [];
|
||||
|
||||
13
source/merge-deep.d.ts
vendored
13
source/merge-deep.d.ts
vendored
@ -8,12 +8,21 @@ import type {
|
||||
UnknownArrayOrTuple,
|
||||
} from './internal/index.d.ts';
|
||||
import type {NonEmptyTuple} from './non-empty-tuple.d.ts';
|
||||
import type {ArrayTail} from './array-tail.d.ts';
|
||||
import type {ArrayTail as _ArrayTail} from './array-tail.d.ts';
|
||||
import type {UnknownRecord} from './unknown-record.d.ts';
|
||||
import type {EnforceOptional} from './enforce-optional.d.ts';
|
||||
import type {SimplifyDeep} from './simplify-deep.d.ts';
|
||||
import type {UnknownArray} from './unknown-array.d.ts';
|
||||
|
||||
type Writable<TArray extends UnknownArray> = {-readonly [Key in keyof TArray]: TArray[Key]}; // TODO: Remove this
|
||||
|
||||
// Using the default `ArrayTail` type causes issues, refer https://github.com/sindresorhus/type-fest/pull/1175/files#r2134694728.
|
||||
type ArrayTail<TArray extends UnknownArray> = TArray extends unknown // For distributing `TArray`
|
||||
? keyof TArray & `${number}` extends never
|
||||
? []
|
||||
: Writable<_ArrayTail<TArray>>
|
||||
: never; // Should never happen
|
||||
|
||||
type SimplifyDeepExcludeArray<T> = SimplifyDeep<T, UnknownArray>;
|
||||
|
||||
/**
|
||||
@ -86,7 +95,7 @@ type OmitRestTypeHelper<
|
||||
Tail extends UnknownArrayOrTuple,
|
||||
Type extends UnknownArrayOrTuple,
|
||||
Result extends UnknownArrayOrTuple = [],
|
||||
> = Tail extends readonly []
|
||||
> = Tail extends []
|
||||
? Result
|
||||
: OmitRestType<Tail, [...Result, FirstArrayElement<Type>]>;
|
||||
|
||||
|
||||
@ -11,22 +11,14 @@ expectType<readonly []>(getArrayTail([] as const));
|
||||
expectType<readonly []>(getArrayTail(['a'] as const));
|
||||
expectType<readonly ['b', 'c']>(getArrayTail(['a', 'b', 'c'] as const));
|
||||
|
||||
// Optional elements tests
|
||||
expectType<readonly [undefined, 'c']>(getArrayTail(['a', undefined, 'c'] as const));
|
||||
|
||||
// Mixed optional/required
|
||||
type MixedArray = [string, undefined?, number?];
|
||||
expectType<[undefined?, number?]>(getArrayTail(['hello'] as MixedArray));
|
||||
|
||||
// Optional numbers
|
||||
expectType<readonly [undefined, 3]>(getArrayTail([1, undefined, 3] as const));
|
||||
|
||||
// Complex mixed case
|
||||
type ComplexArray = [string, boolean, number?, string?];
|
||||
expectType<[boolean, number?, string?]>(getArrayTail(['test', false] as ComplexArray));
|
||||
// Optional elements
|
||||
expectType<readonly [undefined, 'c'?]>(getArrayTail(['a', undefined, 'c'] as readonly ['a', undefined, 'c'?]));
|
||||
expectType<[undefined?, number?]>(getArrayTail(['hello'] as [string, undefined?, number?]));
|
||||
expectType<readonly [undefined?, 3?]>(getArrayTail([1, undefined, 3] as readonly [1, undefined?, 3?]));
|
||||
expectType<[boolean, number?, string?]>(getArrayTail(['test', false] as [string, boolean, number?, string?]));
|
||||
|
||||
// All optional elements
|
||||
expectType<['b'?]>([] as ArrayTail<['a'?, 'b'?]>);
|
||||
expectType<['b'?]>({} as ArrayTail<['a'?, 'b'?]>);
|
||||
expectType<readonly [number?]>({} as ArrayTail<readonly [string?, number?]>);
|
||||
|
||||
// Rest element
|
||||
@ -36,14 +28,23 @@ expectType<readonly [number, boolean?, ...string[]]>({} as ArrayTail<readonly [s
|
||||
// expectType<readonly [...string[], string, number]>({} as ArrayTail<readonly [...string[], string, number]>); // Rest & Required
|
||||
expectType<readonly [number, ...string[], boolean, bigint]>({} as ArrayTail<readonly [string, number, ...string[], boolean, bigint]>); // Required, Rest & Required
|
||||
|
||||
// Labelled tuples
|
||||
expectType<[y: string]>({} as ArrayTail<[x: number, y: string]>);
|
||||
expectType<[bar: string, ...rest: boolean[]]>({} as ArrayTail<[foo: number, bar: string, ...rest: boolean[]]>);
|
||||
expectType<[...rest: boolean[], foo: number, bar: string]>({} as ArrayTail<[...rest: boolean[], foo: number, bar: string]>);
|
||||
|
||||
// Union of tuples
|
||||
expectType<[] | ['b']>([] as ArrayTail<[] | ['a', 'b']>);
|
||||
expectType<readonly ['y'?] | ['b', ...string[]] | readonly []>([] as ArrayTail<readonly ['x'?, 'y'?] | ['a', 'b', ...string[]] | readonly string[]>);
|
||||
expectType<[] | ['b']>({} as ArrayTail<[] | ['a', 'b']>);
|
||||
expectType<readonly ['y'?] | ['b', ...string[]] | readonly string[]>({} as ArrayTail<readonly ['x'?, 'y'?] | ['a', 'b', ...string[]] | readonly string[]>);
|
||||
expectType<[number] | readonly [boolean, string?]>({} as ArrayTail<[string, number] | readonly [number, boolean, string?]>);
|
||||
expectType<readonly [number] | readonly []>({} as ArrayTail<readonly [string, number] | readonly string[]>);
|
||||
expectType<readonly [number] | readonly string[]>({} as ArrayTail<readonly [string, number] | readonly string[]>);
|
||||
|
||||
// Non tuple arrays
|
||||
expectType<[]>({} as ArrayTail<string[]>);
|
||||
expectType<readonly []>({} as ArrayTail<readonly string[]>);
|
||||
expectType<[]>({} as ArrayTail<never[]>);
|
||||
expectType<[]>({} as ArrayTail<any[]>);
|
||||
expectType<string[]>({} as ArrayTail<string[]>);
|
||||
expectType<readonly string[]>({} as ArrayTail<readonly string[]>);
|
||||
expectType<never[]>({} as ArrayTail<never[]>);
|
||||
expectType<any[]>({} as ArrayTail<any[]>);
|
||||
|
||||
// Boundary cases
|
||||
expectType<never>({} as ArrayTail<never>);
|
||||
expectType<any>({} as ArrayTail<any>);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user