Skip to content

Commit 7fb2f75

Browse files
authored
Add TupleOf type (#1247)
1 parent 3ac9f35 commit 7fb2f75

File tree

14 files changed

+130
-49
lines changed

14 files changed

+130
-49
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ export type {IsLowercase} from './source/is-lowercase.d.ts';
157157
export type {IsUppercase} from './source/is-uppercase.d.ts';
158158
export type {IsOptional} from './source/is-optional.d.ts';
159159
export type {IsNullable} from './source/is-nullable.d.ts';
160+
export type {TupleOf} from './source/tuple-of.d.ts';
160161

161162
// Template literal types
162163
export type {CamelCase, CamelCaseOptions} from './source/camel-case.d.ts';

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ Click the type names for complete docs.
256256
- [`TupleToUnion`](source/tuple-to-union.d.ts) - Convert a tuple/array into a union type of its elements.
257257
- [`UnionToTuple`](source/union-to-tuple.d.ts) - Convert a union type into an unordered tuple type of its elements.
258258
- [`TupleToObject`](source/tuple-to-object.d.ts) - Transforms a tuple into an object, mapping each tuple index to its corresponding type as a key-value pair.
259+
- [`TupleOf`](source/tuple-of.d.ts) - Creates a tuple type of the specified length with elements of the specified type.
259260

260261
### Numeric
261262

source/array-splice.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import type {BuildTuple, StaticPartOfArray, VariablePartOfArray} from './internal/index.d.ts';
1+
import type {StaticPartOfArray, VariablePartOfArray} from './internal/index.d.ts';
22
import type {GreaterThanOrEqual} from './greater-than-or-equal.d.ts';
33
import type {Subtract} from './subtract.d.ts';
44
import type {UnknownArray} from './unknown-array.d.ts';
5+
import type {TupleOf} from './tuple-of.d.ts';
56

67
/**
78
The implementation of `SplitArrayByIndex` for fixed length arrays.
89
*/
910
type SplitFixedArrayByIndex<T extends UnknownArray, SplitIndex extends number> =
1011
SplitIndex extends 0
1112
? [[], T]
12-
: T extends readonly [...BuildTuple<SplitIndex>, ...infer V]
13+
: T extends readonly [...TupleOf<SplitIndex>, ...infer V]
1314
? T extends readonly [...infer U, ...V]
1415
? [U, V]
1516
: [never, never]
@@ -22,7 +23,7 @@ type SplitVariableArrayByIndex<T extends UnknownArray,
2223
SplitIndex extends number,
2324
T1 = Subtract<SplitIndex, StaticPartOfArray<T>['length']>,
2425
T2 = T1 extends number
25-
? BuildTuple<GreaterThanOrEqual<T1, 0> extends true ? T1 : number, VariablePartOfArray<T>[number]>
26+
? TupleOf<GreaterThanOrEqual<T1, 0> extends true ? T1 : number, VariablePartOfArray<T>[number]>
2627
: [],
2728
> =
2829
SplitIndex extends 0

source/int-range.d.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {BuildTuple} from './internal/index.d.ts';
1+
import type {TupleOf} from './tuple-of.d.ts';
22
import type {Subtract} from './subtract.d.ts';
33

44
/**
@@ -44,16 +44,16 @@ type PrivateIntRange<
4444
// The gap between each number, gap = step - 1
4545
Gap extends number = Subtract<Step, 1>,
4646
// The final `List` is `[...StartLengthTuple, ...[number, ...GapLengthTuple], ...[number, ...GapLengthTuple], ... ...]`, so can initialize the `List` with `[...StartLengthTuple]`
47-
List extends unknown[] = BuildTuple<Start, never>,
48-
EndLengthTuple extends unknown[] = BuildTuple<End>,
47+
List extends unknown[] = TupleOf<Start, never>,
48+
EndLengthTuple extends unknown[] = TupleOf<End>,
4949
> = Gap extends 0 ?
5050
// Handle the case that without `Step`
5151
List['length'] extends End // The result of "List[length] === End"
5252
? Exclude<List[number], never> // All unused elements are `never`, so exclude them
5353
: PrivateIntRange<Start, End, Step, Gap, [...List, List['length'] ]>
5454
// Handle the case that with `Step`
55-
: List extends [...(infer U), ...EndLengthTuple] // The result of "List[length] >= End", because the `...BuildTuple<Gap, never>` maybe make `List` too long.
55+
: List extends [...(infer U), ...EndLengthTuple] // The result of "List[length] >= End", because the `...TupleOf<Gap, never>` maybe make `List` too long.
5656
? Exclude<List[number], never>
57-
: PrivateIntRange<Start, End, Step, Gap, [...List, List['length'], ...BuildTuple<Gap, never>]>;
57+
: PrivateIntRange<Start, End, Step, Gap, [...List, List['length'], ...TupleOf<Gap, never>]>;
5858

5959
export {};

source/internal/string.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import type {TupleOf} from '../tuple-of.d.ts';
12
import type {NegativeInfinity, PositiveInfinity} from '../numeric.d.ts';
23
import type {Trim} from '../trim.d.ts';
34
import type {Whitespace} from './characters.d.ts';
4-
import type {BuildTuple} from './tuple.d.ts';
55

66
/**
77
Return a string representation of the given string or number.
@@ -171,7 +171,7 @@ PositiveNumericStringGt<'1', '500'>;
171171
*/
172172
export type PositiveNumericStringGt<A extends string, B extends string> = A extends B
173173
? false
174-
: [BuildTuple<StringLength<A>, 0>, BuildTuple<StringLength<B>, 0>] extends infer R extends [readonly unknown[], readonly unknown[]]
174+
: [TupleOf<StringLength<A>, 0>, TupleOf<StringLength<B>, 0>] extends infer R extends [readonly unknown[], readonly unknown[]]
175175
? R[0] extends [...R[1], ...infer Remain extends readonly unknown[]]
176176
? 0 extends Remain['length']
177177
? SameLengthPositiveNumericStringGt<A, B>

source/internal/tuple.d.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,6 @@ export type TupleLength<T extends UnknownArray> =
3030
: T['length']
3131
: never; // Should never happen
3232

33-
/**
34-
Create a tuple type of the given length `<L>` and fill it with the given type `<Fill>`.
35-
36-
If `<Fill>` is not provided, it will default to `unknown`.
37-
38-
@link https://itnext.io/implementing-arithmetic-within-typescripts-type-system-a1ef140a6f6f
39-
*/
40-
export type BuildTuple<L extends number, Fill = unknown, T extends readonly unknown[] = []> = number extends L
41-
? Fill[]
42-
: L extends T['length']
43-
? T
44-
: BuildTuple<L, Fill, [...T, Fill]>;
45-
4633
/**
4734
Returns the maximum value from a tuple of integers.
4835

source/pick-deep.d.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type {BuildObject, BuildTuple, NonRecursiveType, ObjectValue} from './internal/index.d.ts';
1+
import type {TupleOf} from './tuple-of.d.ts';
2+
import type {BuildObject, NonRecursiveType, ObjectValue} from './internal/index.d.ts';
23
import type {IsNever} from './is-never.d.ts';
34
import type {Paths} from './paths.d.ts';
45
import type {Simplify} from './simplify.d.ts';
@@ -131,9 +132,9 @@ type PickDeepArray<ArrayType extends UnknownArray, P extends string | number> =
131132
: never
132133
// When `ArrayIndex` is a number literal
133134
: ArrayType extends unknown[]
134-
? [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
135+
? [...TupleOf<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
135136
: ArrayType extends readonly unknown[]
136-
? readonly [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
137+
? readonly [...TupleOf<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
137138
: never
138139
// When the path is equal to `number`
139140
: P extends `${infer ArrayIndex extends number}`
@@ -142,9 +143,9 @@ type PickDeepArray<ArrayType extends UnknownArray, P extends string | number> =
142143
? ArrayType
143144
// When `ArrayIndex` is a number literal
144145
: ArrayType extends unknown[]
145-
? [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
146+
? [...TupleOf<ArrayIndex>, ArrayType[ArrayIndex]]
146147
: ArrayType extends readonly unknown[]
147-
? readonly [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
148+
? readonly [...TupleOf<ArrayIndex>, ArrayType[ArrayIndex]]
148149
: never
149150
: never;
150151

source/subtract.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import type {NumberAbsolute, BuildTuple, ReverseSign} from './internal/index.d.ts';
1+
import type {NumberAbsolute, ReverseSign} from './internal/index.d.ts';
22
import type {PositiveInfinity, NegativeInfinity, IsNegative} from './numeric.d.ts';
33
import type {LessThan} from './less-than.d.ts';
4+
import type {TupleOf} from './tuple-of.d.ts';
45

56
/**
67
Returns the difference between two numbers.
@@ -60,7 +61,7 @@ type SubtractPostChecks<A extends number, B extends number, AreNegative = [IsNeg
6061
// When both numbers are negative we subtract the absolute values and then reverse the sign
6162
? ReverseSign<SubtractPositives<NumberAbsolute<A>, NumberAbsolute<B>>>
6263
// When the signs are different we can add the absolute values and then reverse the sign if A < B
63-
: [...BuildTuple<NumberAbsolute<A>>, ...BuildTuple<NumberAbsolute<B>>] extends infer R extends unknown[]
64+
: [...TupleOf<NumberAbsolute<A>>, ...TupleOf<NumberAbsolute<B>>] extends infer R extends unknown[]
6465
? LessThan<A, B> extends true ? ReverseSign<R['length']> : R['length']
6566
: never;
6667

@@ -78,7 +79,7 @@ Subtracts two positive numbers A and B such that A > B.
7879
*/
7980
type SubtractIfAGreaterThanB<A extends number, B extends number> =
8081
// This is where we always want to end up and do the actual subtraction
81-
BuildTuple<A> extends [...BuildTuple<B>, ...infer R]
82+
TupleOf<A> extends [...TupleOf<B>, ...infer R]
8283
? R['length']
8384
: never;
8485

source/sum.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type {NumberAbsolute, BuildTuple, TupleMax, ReverseSign} from './internal/index.d.ts';
1+
import type {TupleOf} from './tuple-of.d.ts';
2+
import type {NumberAbsolute, TupleMax, ReverseSign} from './internal/index.d.ts';
23
import type {PositiveInfinity, NegativeInfinity, IsNegative} from './numeric.d.ts';
34
import type {Subtract} from './subtract.d.ts';
45

@@ -73,7 +74,7 @@ type SumPostChecks<A extends number, B extends number, AreNegative = [IsNegative
7374
Adds two positive numbers.
7475
*/
7576
type SumPositives<A extends number, B extends number> =
76-
[...BuildTuple<A>, ...BuildTuple<B>]['length'] extends infer Result extends number
77+
[...TupleOf<A>, ...TupleOf<B>]['length'] extends infer Result extends number
7778
? Result
7879
: never;
7980

source/tuple-of.d.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type {If} from './if.d.ts';
2+
import type {IfNotAnyOrNever} from './internal/type.d.ts';
3+
import type {IsNegative} from './numeric.d.ts';
4+
import type {UnknownArray} from './unknown-array.d.ts';
5+
6+
/**
7+
Creates a tuple type of the specified length with elements of the specified type.
8+
9+
@example
10+
```
11+
import type {TupleOf} from 'type-fest';
12+
13+
type RGB = TupleOf<3, number>;
14+
//=> [number, number, number]
15+
16+
type Line = TupleOf<2, {x: number; y: number}>;
17+
//=> [{x: number; y: number}, {x: number; y: number}]
18+
19+
type TicTacToeBoard = TupleOf<3, TupleOf<3, 'X' | 'O' | null>>;
20+
//=> [['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null], ['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null], ['X' | 'O' | null, 'X' | 'O' | null, 'X' | 'O' | null]]
21+
```
22+
23+
Note: If the specified length is the non-literal `number` type, the result will not be a tuple but a regular array.
24+
25+
@example
26+
```
27+
import type {TupleOf} from 'type-fest';
28+
29+
type StringArray = TupleOf<number, string>;
30+
//=> string[]
31+
```
32+
33+
Note: If the type for elements is not specified, it will default to `unknown`.
34+
35+
@example
36+
```
37+
import type {TupleOf} from 'type-fest';
38+
39+
type UnknownTriplet = TupleOf<3>;
40+
//=> [unknown, unknown, unknown]
41+
```
42+
43+
Note: If the specified length is negative, the result will be an empty tuple.
44+
45+
@example
46+
```
47+
import type {TupleOf} from 'type-fest';
48+
49+
type EmptyTuple = TupleOf<-3, string>;
50+
//=> []
51+
```
52+
53+
@category Array
54+
*/
55+
export type TupleOf<Length extends number, Fill = unknown> = IfNotAnyOrNever<Length,
56+
_TupleOf<If<IsNegative<Length>, 0, Length>, Fill, []>,
57+
Fill[], []>;
58+
59+
type _TupleOf<L extends number, Fill, Accumulator extends UnknownArray> = number extends L
60+
? Fill[]
61+
: L extends Accumulator['length']
62+
? Accumulator
63+
: _TupleOf<L, Fill, [...Accumulator, Fill]>;
64+
65+
export {};

0 commit comments

Comments
 (0)