Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 5 additions & 1 deletion source/last-array-element.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ export type LastArrayElement<Elements extends readonly unknown[], ElementBeforeT
// 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
? number extends Elements['length']
// A genuine non-tuple array (e.g., `number[]`) or trailing spread element — the last element can be the rest type or the element before it.
? U | ElementBeforeTailingSpreadElement
// A tuple consisting solely of optional elements (e.g., `[string?, number?]`). The last element is the type of any of those elements (without the `| undefined` added by the optional modifier) or, when they are all absent, the element before them.
: Exclude<U, undefined> | ElementBeforeTailingSpreadElement
: never;

export {};
23 changes: 23 additions & 0 deletions test-d/last-array-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,26 @@ expectType<number | boolean>(lastOf(trailingSpreadTuple2));
// eslint-disable-next-line @typescript-eslint/array-type
declare const trailingSpreadTuple3: ['foo', true, ...(1 | '2')[]];
expectType<true | 1 | '2'>(lastOf(trailingSpreadTuple3));

// Tuples with optional elements
declare const trailingOptionalTuple1: [string, number?];
expectType<string | number>(lastOf(trailingOptionalTuple1));

declare const trailingOptionalTuple2: [number?];
expectType<number>(lastOf(trailingOptionalTuple2));

declare const trailingOptionalTuple3: [string, boolean, number?];
expectType<boolean | number>(lastOf(trailingOptionalTuple3));

declare const trailingOptionalTuple4: [string, number?, boolean?];
expectType<string | number | boolean>(lastOf(trailingOptionalTuple4));

declare const leadingOptionalTuple: [string?, number?];
expectType<string | number>(lastOf(leadingOptionalTuple));

declare const readonlyOptionalTuple: readonly [string, number?];
expectType<string | number>(lastOf(readonlyOptionalTuple));

// The `| undefined` from a genuine `undefined`-containing array is preserved
declare const undefinedArray: Array<number | undefined>;
expectType<number | undefined>(lastOf(undefinedArray));

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Suggested change
expectType<number | undefined>(lastOf(undefinedArray));
expectType<number | undefined>(lastOf(undefinedArray));
declare const optionalExplicitUndefinedTuple: [string, (number | undefined)?];
expectType<string | number | undefined>(lastOf(optionalExplicitUndefinedTuple));
declare const optionalExplicitUndefinedOnlyTuple: [(number | undefined)?];
expectType<number | undefined>(lastOf(optionalExplicitUndefinedOnlyTuple));
declare const optionalBeforeRestTuple: [string, number?, ...boolean[]];
expectType<string | number | boolean>(lastOf(optionalBeforeRestTuple));