Skip to content

Commit c8149ec

Browse files
authored
Add TupleToObject type (#1055)
1 parent becc1ff commit c8149ec

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export type {HasWritableKeys} from './source/has-writable-keys';
104104
export type {Spread} from './source/spread';
105105
export type {IsInteger} from './source/is-integer';
106106
export type {IsFloat} from './source/is-float';
107+
export type {TupleToObject} from './source/tuple-to-object';
107108
export type {TupleToUnion} from './source/tuple-to-union';
108109
export type {UnionToTuple} from './source/union-to-tuple';
109110
export type {IntRange} from './source/int-range';

readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ type ShouldBeNever = IfAny<'not any', 'not never', 'never'>;
292292
- [`ReadonlyTuple`](source/readonly-tuple.d.ts) - Create a type that represents a read-only tuple of the given type and length.
293293
- [`TupleToUnion`](source/tuple-to-union.d.ts) - Convert a tuple/array into a union type of its elements.
294294
- [`UnionToTuple`](source/union-to-tuple.d.ts) - Convert a union type into an unordered tuple type of its elements.
295+
- [`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.
295296

296297
### Numeric
297298

source/tuple-to-object.d.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type {IsTuple} from './is-tuple';
2+
import type {UnknownArray} from './unknown-array';
3+
import type {IfAny} from './if-any';
4+
5+
/**
6+
Transforms a tuple into an object, mapping each tuple index to its corresponding type as a key-value pair.
7+
8+
Note: Tuple labels are [lost in the transformation process](https://stackoverflow.com/a/70398429/11719314). For example, `TupleToObject<[x: number, y: number]>` produces `{0: number; 1: number}`, and not `{x: number; y: number}`.
9+
10+
@example
11+
```
12+
type Example1 = TupleToObject<[number, string, boolean]>;
13+
//=> { 0: number; 1: string; 2: boolean }
14+
15+
// Tuples with optional indices
16+
type Example2 = TupleToObject<[number, string?, boolean?]>;
17+
//=> { 0: number; 1?: string; 2?: boolean }
18+
19+
// Readonly tuples
20+
type Example3 = TupleToObject<readonly [number, string?]>;
21+
//=> { readonly 0: number; readonly 1?: string }
22+
23+
// Non-tuple arrays get transformed into index signatures
24+
type Example4 = TupleToObject<string[]>;
25+
//=> { [x: number]: string }
26+
27+
// Tuples with rest elements
28+
type Example5 = TupleToObject<[number, string, ...boolean[]]>;
29+
//=> { [x: number]: number | string | boolean; 0: number; 1: string }
30+
31+
// Tuple labels are not preserved
32+
type Example6 = TupleToObject<[x: number, y: number]>;
33+
//=> { 0: number; 1: number }
34+
```
35+
36+
@category Array
37+
*/
38+
export type TupleToObject<TArray extends UnknownArray> = IfAny<TArray, any, {
39+
[
40+
Key in keyof TArray as Key & (`${number}` | (IsTuple<TArray> extends true ? never : number))
41+
]: TArray[Key];
42+
}>;

test-d/tuple-to-object.ts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import {expectType} from 'tsd';
2+
import type {TupleToObject} from '../source/tuple-to-object';
3+
4+
// Tuples
5+
expectType<TupleToObject<[]>>({} as {});
6+
expectType<TupleToObject<[number]>>({} as {0: number});
7+
expectType<TupleToObject<[number, string]>>({} as {0: number; 1: string});
8+
expectType<TupleToObject<[number | string, {foo: string}]>>({} as {0: number | string; 1: {foo: string}});
9+
expectType<TupleToObject<[number, string?]>>({} as {0: number; 1?: string});
10+
expectType<TupleToObject<[number?, string?, boolean?]>>({} as {0?: number; 1?: string; 2?: boolean});
11+
12+
// Readonly tuples
13+
expectType<TupleToObject<readonly []>>({} as {});
14+
expectType<TupleToObject<readonly [number, string]>>({} as {readonly 0: number; readonly 1: string});
15+
expectType<TupleToObject<readonly [number, string?, boolean?]>>({} as {readonly 0: number; readonly 1?: string; readonly 2?: boolean});
16+
17+
// Non-tuples
18+
expectType<TupleToObject<number[]>>({} as Record<number, number>);
19+
expectType<TupleToObject<[...boolean[]]>>({} as Record<number, boolean>);
20+
expectType<TupleToObject<[number, string, ...boolean[]]>>({} as {[x: number]: number | string | boolean; 0: number; 1: string});
21+
expectType<TupleToObject<[number, string?, ...boolean[]]>>({} as {[x: number]: number | string | boolean | undefined; 0: number; 1?: string});
22+
expectType<TupleToObject<[...number[], string, bigint]>>({} as Record<number, number | string | bigint>);
23+
expectType<TupleToObject<never[]>>({} as Record<number, never>);
24+
expectType<TupleToObject<any[]>>({} as Record<number, any>);
25+
26+
// Readonly non-tuples
27+
expectType<TupleToObject<readonly number[]>>({} as Readonly<Record<number, number>>);
28+
expectType<TupleToObject<readonly [number, string?, ...boolean[]]>>({} as {readonly [x: number]: number | string | boolean | undefined; readonly 0: number; readonly 1?: string});
29+
expectType<TupleToObject<readonly [...number[], string, bigint]>>({} as Readonly<Record<number, number | string | bigint>>);
30+
31+
// Unions
32+
expectType<TupleToObject<[number] | [number, string, boolean]>>(
33+
{} as {0: number} | {0: number; 1: string; 2: boolean},
34+
);
35+
expectType<TupleToObject<[number?, string?] | [] | [number, string, ...number[]]>>(
36+
{} as {0?: number; 1?: string} | {} | {[x: number]: number | string; 0: number; 1: string},
37+
);
38+
expectType<TupleToObject<Array<number | undefined> | string[]>>(
39+
{} as Record<number, number | undefined> | Record<number, string>,
40+
);
41+
expectType<TupleToObject<[number, string] | readonly string[]>>(
42+
{} as {0: number; 1: string} | Readonly<Record<number, string>>,
43+
);
44+
expectType<TupleToObject<readonly [string] | readonly [number?] | number[]>>(
45+
{} as {readonly 0: string} | {readonly 0?: number} | Record<number, number>,
46+
);
47+
expectType<TupleToObject<[...number[], string] | [number?, string?, ...number[]]>>(
48+
{} as Record<number, string | number> | {[x: number]: string | number | undefined; 0?: number; 1?: string},
49+
);
50+
51+
// Labeled tuples
52+
expectType<TupleToObject<[x: string, y: number]>>({} as {0: string; 1: number});
53+
expectType<TupleToObject<[first: string, ...rest: number[]]>>({} as {[x: number]: string | number; 0: string});
54+
expectType<TupleToObject<[...rest: number[], last: string]>>({} as Record<number, string | number>);
55+
expectType<TupleToObject<readonly [name: string, age?: number]>>({} as {readonly 0: string; readonly 1?: number});
56+
57+
// Boundary types
58+
expectType<TupleToObject<any>>({} as any);
59+
expectType<TupleToObject<never>>({} as never);
60+
61+
// @ts-expect-error only works with arrays
62+
type T = TupleToObject<{}>;

0 commit comments

Comments
 (0)