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
8 changes: 5 additions & 3 deletions source/exclude-rest-element.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {SplitOnRestElement} from './split-on-rest-element.d.ts';
import type {IsArrayReadonly} from './internal/array.d.ts';
import type {UnknownArray} from './unknown-array.d.ts';
import type {IfNotAnyOrNever} from './internal/type.d.ts';

/**
Create a tuple with the [`rest`](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) element removed.
Expand All @@ -26,13 +27,14 @@ type T4 = ExcludeRestElement<[number, string]>;
@see {@link SplitOnRestElement}
@category Array
*/
export type ExcludeRestElement<Array_ extends UnknownArray> =
export type ExcludeRestElement<Array_ extends UnknownArray> = IfNotAnyOrNever<Array_,
SplitOnRestElement<Array_> extends infer Result
? Result extends readonly UnknownArray[]
? IsArrayReadonly<Array_> extends true
? Readonly<[...Result[0], ...Result[2]]>
: [...Result[0], ...Result[2]]
: Result
: never;
: 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.

If this is Result (and not never), then the compiler thinks the type produces something more than just arrays.

: never
>;
Comment on lines +30 to +38
Copy link
Copy Markdown
Contributor

@benzaria benzaria Oct 17, 2025

Choose a reason for hiding this comment

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

Please consider using IsAnyOrNever instead, as it provides more reliable handling for recursive types.
Refer my #1192 comment, and your #1266 comment regarding If* types.

Suggested change
export type ExcludeRestElement<Array_ extends UnknownArray> = IfNotAnyOrNever<Array_,
SplitOnRestElement<Array_> extends infer Result
? Result extends readonly UnknownArray[]
? IsArrayReadonly<Array_> extends true
? Readonly<[...Result[0], ...Result[2]]>
: [...Result[0], ...Result[2]]
: Result
: never;
: never
: never
>;
export type ExcludeRestElement<Array_ extends UnknownArray> =
IsAnyOrNever<Array_> extends true ? Array_
: SplitOnRestElement<Array_> extends infer Result
? Result extends readonly UnknownArray[]
? IsArrayReadonly<Array_> extends true
? Readonly<[...Result[0], ...Result[2]]>
: [...Result[0], ...Result[2]]
: never
: never;

Copy link
Copy Markdown
Collaborator Author

@som-sm som-sm Oct 17, 2025

Choose a reason for hiding this comment

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

IfNotAnyOrNever works perfectly fine in this case because ExcludeRestElement is not a recursive type. Added test cases for long tuples in #0aed831.


your #1266 comment regarding If* types.

This was only for recursive types.

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.

And, if used properly, IfNotAnyOrNever can even be used with recursive types without any problems. Added a note in #1276.


export {};
13 changes: 12 additions & 1 deletion test-d/exclude-rest-element.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expectType} from 'tsd';
import type {ExcludeRestElement} from '../index.d.ts';
import type {ExcludeRestElement, TupleOf, UnknownArray} from '../index.d.ts';

// Basic static tuples (No rest element)
expectType<ExcludeRestElement<[]>>({} as []);
Expand Down Expand Up @@ -51,3 +51,14 @@ expectType<ExcludeRestElement<[['a'], ...string[], ['z']]>>({} as [['a'], ['z']]
// Edge: `never` / `any`
expectType<ExcludeRestElement<any>>({} as any);
expectType<ExcludeRestElement<never>>({} as never);

// Long tuples
type FiftyZeroes = TupleOf<50, '0'>;
expectType<ExcludeRestElement<[...FiftyZeroes, ...number[]]>>({} as FiftyZeroes);

type NineHundredNinetyNineZeroes = TupleOf<999, '0'>;
expectType<ExcludeRestElement<[...NineHundredNinetyNineZeroes, ...number[]]>>({} as NineHundredNinetyNineZeroes);

// Generic instantiations
type Assignability<_T extends UnknownArray> = unknown;
type TestAssignability<T extends UnknownArray> = Assignability<ExcludeRestElement<T>>;
9 changes: 8 additions & 1 deletion test-d/extract-rest-element.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expectType} from 'tsd';
import type {ExtractRestElement} from '../index.d.ts';
import type {ExtractRestElement, TupleOf} from '../index.d.ts';

// Leading rest element
expectType<ExtractRestElement<[...string[], 1]>>({} as string);
Expand Down Expand Up @@ -49,3 +49,10 @@ expectType<ExtractRestElement<[['a'], ...boolean[], ['z']]>>({} as boolean);
// Edge: `never` / `any`
expectType<ExtractRestElement<any>>({} as any);
expectType<ExtractRestElement<never>>({} as never);

// Long tuples
type FiftyZeroes = TupleOf<50, '0'>;
expectType<ExtractRestElement<[...FiftyZeroes, ...number[]]>>({} as number);

type NineHundredNinetyNineZeroes = TupleOf<999, '0'>;
expectType<ExtractRestElement<[...NineHundredNinetyNineZeroes, ...number[]]>>({} as number);
13 changes: 12 additions & 1 deletion test-d/split-on-rest-element.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expectType} from 'tsd';
import type {SplitOnRestElement} from '../index.d.ts';
import type {SplitOnRestElement, TupleOf, UnknownArray} from '../index.d.ts';

// Fixed tuples (No rest element)
expectType<SplitOnRestElement<[]>>({} as [[], [], []]);
Expand Down Expand Up @@ -53,3 +53,14 @@ expectType<SplitOnRestElement<readonly [1, 2, 3]>>({} as readonly [[1, 2, 3], []
// Edge: `never` / `any`
expectType<SplitOnRestElement<any>>({} as any);
expectType<SplitOnRestElement<never>>({} as never);

// Long tuples
type FiftyZeroes = TupleOf<50, '0'>;
expectType<SplitOnRestElement<[...FiftyZeroes, ...number[], ...FiftyZeroes]>>({} as [FiftyZeroes, number[], FiftyZeroes]);

type FourHundredNinetyNineZeroes = TupleOf<499, '0'>;
expectType<SplitOnRestElement<[...FourHundredNinetyNineZeroes, ...number[], ...FourHundredNinetyNineZeroes]>>({} as [FourHundredNinetyNineZeroes, number[], FourHundredNinetyNineZeroes]);

// Generic instantiations
type Assignability<_T extends UnknownArray> = unknown;
type TestAssignability<T extends UnknownArray> = Assignability<SplitOnRestElement<T>>;