Skip to content

Commit

Permalink
add array-map for keeping tuple length when using .map
Browse files Browse the repository at this point in the history
  • Loading branch information
tjenkinson committed Apr 9, 2023
1 parent 4d44ea0 commit d6702f9
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 0 deletions.
5 changes: 5 additions & 0 deletions package.json
Expand Up @@ -72,6 +72,11 @@
"types": "./dist/array-index-of.d.ts",
"import": "./dist/array-index-of.mjs",
"default": "./dist/array-index-of.js"
},
"./array-map": {
"types": "./dist/array-map.d.ts",
"import": "./dist/array-map.mjs",
"default": "./dist/array-map.js"
}
},
"keywords": [],
Expand Down
32 changes: 32 additions & 0 deletions readme.md
Expand Up @@ -7,6 +7,7 @@ TypeScript's built-in typings are not perfect. `ts-reset` makes them better.
- 🚨 `.json` (in `fetch`) and `JSON.parse` both return `any`
- 🤦 `.filter(Boolean)` doesn't behave how you expect
- 😡 `array.includes` often breaks on readonly arrays
- 😭 `array.map` on a tuple looses the tuple length

`ts-reset` smooths over these hard edges, just like a CSS reset does in the browser.

Expand Down Expand Up @@ -293,6 +294,37 @@ const validate = (input: unknown) => {
};
```

### Keeping the tuple length in resulting tuple from `Array.map`

```ts
import "@total-typescript/ts-reset/array-map";
```

When you're using `Array.map` with a tuple, the length is lost. This means you loose the guard against accessing an item out of bounds.

```ts
// BEFORE

const tuple = [1, 2, 3] as const;
const mapped = tuple.map((a) => a + 1);

// oops. There's no 4th element, but no error
console.log(tuple[3]);
```

With `array-map` enabled, this code will now error:

```ts
// AFTER
import "@total-typescript/ts-reset/array-map";

const tuple = [1, 2, 3] as const;
const mapped = tuple.map((a) => a + 1);

// Tuple type 'readonly [number, number, number]' of length '3' has no element at index '3'.
console.log(tuple[3]);
```

## Rules we won't add

### `Object.keys`/`Object.entries`
Expand Down
15 changes: 15 additions & 0 deletions src/entrypoints/array-map.d.ts
@@ -0,0 +1,15 @@
/// <reference path="utils.d.ts" />

interface ReadonlyArray<T> {
map<U>(
callbackfn: (value: T, index: number, array: readonly T[]) => U,
thisArg?: any,
): { [K in keyof this]: U };
}

interface Array<T> {
map<U>(
callbackfn: (value: T, index: number, array: T[]) => U,
thisArg?: any,
): { [K in keyof this]: U };
}
1 change: 1 addition & 0 deletions src/entrypoints/recommended.d.ts
Expand Up @@ -6,3 +6,4 @@
/// <reference path="set-has.d.ts" />
/// <reference path="map-has.d.ts" />
/// <reference path="array-index-of.d.ts" />
/// <reference path="array-map.d.ts" />
77 changes: 77 additions & 0 deletions src/tests/array-map.ts
@@ -0,0 +1,77 @@
import { doNotExecute, Equal, Expect } from "./utils";

doNotExecute(async () => {
const tuple = [0, 1] as const;
const mapped = tuple.map(
(
value: (typeof tuple)[number],
index: number,
source: readonly (typeof tuple)[number][],
) => 1,
);

tuple.map(() => 1, {});

type tests = [
Expect<Equal<(typeof mapped)["length"], 2>>,
Expect<Equal<(typeof mapped)[0], number>>,
];

mapped[0];
mapped[1];
// @ts-expect-error
mapped[2];
});

doNotExecute(async () => {
const tuple = [0, 1] as [0, 1];
const mapped = tuple.map(
(
value: (typeof tuple)[number],
index: number,
source: (typeof tuple)[number][],
) => 1,
);

tuple.map(() => 1, {});

type tests = [
Expect<Equal<(typeof mapped)["length"], 2>>,
Expect<Equal<(typeof mapped)[0], number>>,
];

mapped[0];
mapped[1];
// @ts-expect-error
mapped[2];
});

doNotExecute(async () => {
const arr: readonly number[] = [0, 1];
const mapped = arr.map(
(
value: (typeof arr)[number],
index: number,
source: readonly (typeof arr)[number][],
) => 1,
);

arr.map(() => 1, {});

type tests = [Expect<Equal<(typeof mapped)["length"], number>>];
});

doNotExecute(async () => {
const arr: number[] = [0, 1];
const mapped = arr.map(
(
value: (typeof arr)[number],
index: number,
source: (typeof arr)[number][],
) => 1,
);

arr.map(() => 1, {});

type tests = [Expect<Equal<(typeof mapped)["length"], number>>];
});

0 comments on commit d6702f9

Please sign in to comment.