-
-
Notifications
You must be signed in to change notification settings - Fork 567
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Sindre Sorhus <[email protected]>
- Loading branch information
1 parent
3d54627
commit 1e0872d
Showing
4 changed files
with
146 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import type {IfAny} from './if-any'; | ||
import type {IfNever} from './if-never'; | ||
import type {UnknownArray} from './unknown-array'; | ||
|
||
/** | ||
@see {@link IsTuple} | ||
*/ | ||
export type IsTupleOptions = { | ||
/** | ||
Consider only fixed length arrays as tuples. | ||
- When set to `true` (default), arrays with rest elements (e.g., `[1, ...number[]]`) are _not_ considered as tuples. | ||
- When set to `false`, arrays with at least one non-rest element (e.g., `[1, ...number[]]`) are considered as tuples. | ||
@default true | ||
@example | ||
```ts | ||
import type {IsTuple} from 'type-fest'; | ||
type Example1 = IsTuple<[number, ...number[]], {fixedLengthOnly: true}>; | ||
//=> false | ||
type Example2 = IsTuple<[number, ...number[]], {fixedLengthOnly: false}>; | ||
//=> true | ||
``` | ||
*/ | ||
fixedLengthOnly?: boolean; | ||
}; | ||
|
||
/** | ||
Returns a boolean for whether the given array is a tuple. | ||
Use-case: | ||
- If you want to make a conditional branch based on the result of whether an array is a tuple or not. | ||
Note: `IsTuple` returns `boolean` when instantiated with a union of tuple and non-tuple (e.g., `IsTuple<[1, 2] | number[]>`). | ||
@example | ||
```ts | ||
import type {IsTuple} from 'type-fest'; | ||
type Tuple = IsTuple<[1, 2, 3]>; | ||
//=> true | ||
type NotTuple = IsTuple<number[]>; | ||
//=> false | ||
type TupleWithOptionalItems = IsTuple<[1?, 2?]>; | ||
//=> true | ||
type RestItemsNotAllowed = IsTuple<[1, 2, ...number[]]>; | ||
//=> false | ||
type RestItemsAllowed = IsTuple<[1, 2, ...number[]], {fixedLengthOnly: false}>; | ||
//=> true | ||
``` | ||
@see {@link IsTupleOptions} | ||
@category Type Guard | ||
@category Utilities | ||
*/ | ||
export type IsTuple< | ||
TArray extends UnknownArray, | ||
Options extends IsTupleOptions = {fixedLengthOnly: true}, | ||
> = | ||
IfAny<TArray, boolean, IfNever<TArray, false, | ||
TArray extends unknown // For distributing `TArray` | ||
? number extends TArray['length'] | ||
? Options['fixedLengthOnly'] extends false | ||
? IfNever<keyof TArray & `${number}`, | ||
TArray extends readonly [...any, any] ? true : false, // To handle cases where a non-rest element follows a rest element, e.g., `[...number[], number]` | ||
true> | ||
: false | ||
: true | ||
: false | ||
>>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import {expectType} from 'tsd'; | ||
import type {IsTuple} from '../index'; | ||
|
||
// Tuples | ||
expectType<IsTuple<[]>>(true); | ||
expectType<IsTuple<[number]>>(true); | ||
expectType<IsTuple<[number, string]>>(true); | ||
expectType<IsTuple<[number, string, ...number[]], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<[number?]>>(true); | ||
expectType<IsTuple<[number?, string?]>>(true); | ||
expectType<IsTuple<[number?, string?, ...number[]], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<[...number[], string, number], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<[never]>>(true); | ||
|
||
// Readonly tuples | ||
expectType<IsTuple<readonly []>>(true); | ||
expectType<IsTuple<readonly [number]>>(true); | ||
expectType<IsTuple<readonly [number, string, ...number[]], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<readonly [number?, string?, ...number[]], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<readonly [...number[], string, number], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<readonly [number?]>>(true); | ||
expectType<IsTuple<readonly [never]>>(true); | ||
|
||
// Non-tuples | ||
expectType<IsTuple<number[]>>(false); | ||
expectType<IsTuple<readonly number[]>>(false); | ||
expectType<IsTuple<[...number[]]>>(false); | ||
expectType<IsTuple<[number, string, ...number[]]>>(false); | ||
expectType<IsTuple<readonly [number?, string?, ...number[]]>>(false); | ||
expectType<IsTuple<[...number[], string, number]>>(false); | ||
expectType<IsTuple<readonly [...number[], string, number]>>(false); | ||
expectType<IsTuple<never[]>>(false); | ||
expectType<IsTuple<any[]>>(false); | ||
|
||
// Boundary types | ||
expectType<IsTuple<any>>({} as boolean); | ||
expectType<IsTuple<any, {fixedLengthOnly: true}>>({} as boolean); | ||
expectType<IsTuple<never>>(false); | ||
|
||
// Unions | ||
expectType<IsTuple<[number] | [number, string, boolean]>>(true); | ||
expectType<IsTuple<[number?, string?] | [] | [number, string, ...number[]], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<[number?, string?] | [] | [number, string, ...number[]]>>({} as boolean); | ||
expectType<IsTuple<number[] | string[]>>(false); | ||
expectType<IsTuple<[number, string] | string[]>>({} as boolean); | ||
expectType<IsTuple<[string] | [number] | number[]>>({} as boolean); | ||
expectType<IsTuple<[string, ...number[]] | [number?, string?, ...number[]]>>(false); | ||
expectType<IsTuple<[string, ...number[]] | [number?, string?, ...number[]], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<[] | [number] | readonly [string]>>(true); | ||
|
||
// Labeled tuples | ||
expectType<IsTuple<[x: string, y: number]>>(true); | ||
expectType<IsTuple<[first: string, ...rest: number[]]>>(false); | ||
expectType<IsTuple<[first: string, ...rest: number[]], {fixedLengthOnly: false}>>(true); | ||
expectType<IsTuple<readonly [name: string, age?: number]>>(true); | ||
|
||
// Mixed optional/required elements | ||
expectType<IsTuple<[string, number?]>>(true); | ||
expectType<IsTuple<readonly [string, number?, ...boolean[]]>>(false); | ||
expectType<IsTuple<readonly [string, number?, ...boolean[]], {fixedLengthOnly: false}>>(true); | ||
|
||
// Setting `fixedLengthOnly` to `boolean` falls back to it's default value of `true` | ||
expectType<IsTuple<[number, string, ...number[]], {fixedLengthOnly: boolean}>>(false); | ||
|
||
// @ts-expect-error only works with arrays | ||
type T = IsTuple<{}>; |