diff --git a/.changeset/popular-kids-arrive.md b/.changeset/popular-kids-arrive.md
new file mode 100644
index 0000000000..cec23c23a5
--- /dev/null
+++ b/.changeset/popular-kids-arrive.md
@@ -0,0 +1,5 @@
+---
+'type-plus': minor
+---
+
+Add `ArrayPlus.Entries`.
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 02acbaabeb..3315b20ef5 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -30,7 +30,8 @@
"tersify",
"typelevel",
"typepark",
- "unpartial"
+ "unpartial",
+ "unshift"
],
"eslint.enable": true,
"files.watcherExclude": {
@@ -61,5 +62,10 @@
],
"markdown.extension.toc.orderedList": true,
"markdownlint.ignore": ".markdownlintignore",
+ "tsimporter.filesToExclude": [
+ "**/cjs",
+ "**/esm",
+ "**/tslib"
+ ],
"typescript.tsdk": "node_modules/typescript/lib"
}
diff --git a/ts/array/array.ts b/ts/array/array.at.ts
similarity index 74%
rename from ts/array/array.ts
rename to ts/array/array.at.ts
index 1b441f4109..33b4417523 100644
--- a/ts/array/array.ts
+++ b/ts/array/array.at.ts
@@ -20,16 +20,3 @@ export type At = IndexAt, A[I] | undefined>
: Fail
: never
-
-/**
- * Concats two arrays or tuples.
- *
- * alias of: `[...A, ...B]`
- *
- * @alias ArrayPlus.Concat
- *
- * ```ts
- * type R = Concat<[1], [2, 3]> // [1, 2, 3]
- * ```
- */
-export type Concat = [...A, ...B]
diff --git a/ts/array/array.concat.ts b/ts/array/array.concat.ts
new file mode 100644
index 0000000000..f14b30af81
--- /dev/null
+++ b/ts/array/array.concat.ts
@@ -0,0 +1,12 @@
+/**
+ * Concats two arrays or tuples.
+ *
+ * alias of: `[...A, ...B]`
+ *
+ * @alias ArrayPlus.Concat
+ *
+ * ```ts
+ * type R = Concat<[1], [2, 3]> // [1, 2, 3]
+ * ```
+ */
+export type Concat = [...A, ...B]
diff --git a/ts/array/array.entries.spec.ts b/ts/array/array.entries.spec.ts
new file mode 100644
index 0000000000..c1701054f4
--- /dev/null
+++ b/ts/array/array.entries.spec.ts
@@ -0,0 +1,24 @@
+import { it, test } from '@jest/globals'
+import { testType, type ArrayPlus } from '../index.js'
+
+test('behavior of array.entries()', () => {
+ const array = [1, 2, '3']
+ const entries = array.entries()
+ testType.equal>(true)
+})
+
+test('behavior of tuple.entries()', () => {
+ const tuple = [1, 2, '3'] as const
+ const entries = tuple.entries()
+ testType.equal>(true)
+})
+
+it('gets Array<[number, T]> for array', () => {
+ testType.equal, Array<[number, string]>>(true)
+ testType.equal>, Array<[number, string | number]>>(true)
+})
+
+it('returns [[0, T1], [1, T2],...[n, Tn]] for tuple', () => {
+ testType.equal, []>(true)
+ testType.equal, [[0, 1], [1, 2], [2, 3]]>(true)
+})
diff --git a/ts/array/array.entries.ts b/ts/array/array.entries.ts
new file mode 100644
index 0000000000..bd6970ee3b
--- /dev/null
+++ b/ts/array/array.entries.ts
@@ -0,0 +1,24 @@
+import type { IsTuple } from '../tuple/tuple_type.js'
+/**
+ * Returns an array of key-value pairs for every entry in the array or tuple.
+ *
+ * Note that this is not the same as `Array.entries(A)`,
+ * which returns an iterable interator.
+ *
+ * @example
+ * ```ts
+ * ArrayPlus.Entries> // Array<[number, string | number]>
+ * ArrayPlus.Entries<[1, 2, 3]> // [[0, 1], [1, 2], [2, 3]]
+ * ```
+ */
+export type Entries = IsTuple<
+ A,
+ Device,
+ A extends Array ? Array<[number, T]> : never
+>
+
+type Device = A['length'] extends 0
+ ? R
+ : A extends [...infer F, infer N]
+ ? Device
+ : never
diff --git a/ts/array/array_plus.ts b/ts/array/array_plus.ts
index cc828e2a1d..637d9ee9ed 100644
--- a/ts/array/array_plus.ts
+++ b/ts/array/array_plus.ts
@@ -1,2 +1,4 @@
-export type { At, Concat } from './array.js'
+export type { At } from './array.at.js'
+export type { Concat } from './array.concat.js'
+export type { Entries } from './array.entries.js'
export type { IndexAt, IsIndexOutOfBound } from './array_index.js'
diff --git a/ts/array/readme.md b/ts/array/readme.md
index e690f57068..061de9c37d 100644
--- a/ts/array/readme.md
+++ b/ts/array/readme.md
@@ -33,7 +33,7 @@ as *tuples* is a subset of array.
For *tuple* specific types and type utilities,
please check [`TuplePlus`](../tuple/readme.md#TuplePlus).
-### [`ArrayPlus.At`](./array.ts#L17)
+### [`ArrayPlus.At`](./array.at.ts#L18)
> `ArrayPlus.At`
@@ -61,7 +61,7 @@ If the `N` is out of bound,
or `N` is not a valid index,
`ArrayPlus.At` will return the `Fail` case, which defaults to `never`.
-### [`ArrayPlus.Concat`](./array.ts#L35)
+### [`ArrayPlus.Concat`](./array.concat.ts#L12)
> `ArrayPlus.Concat`
@@ -72,6 +72,68 @@ It is added for completeness.
You are encouraged to use `[...A, ...B]` directly.
+### [`ArrayPlus.Entries`](./array.entries.ts#L14)
+
+> `ArrayPlus.Entries`
+
+Returns an array of key-value pairs for every entry in the array or tuple.
+
+Note that this is not the same as `Array.entries(A)`,
+which returns an iterable interator.
+
+```ts
+ArrayPlus.Entries> // Array<[number, string | number]>
+ArrayPlus.Entries<[1, 2, 3]> // [[0, 1], [1, 2], [2, 3]]
+```
+
+## Builtin array methods
+
+JavaScript has many builtin array methods.
+We will try to bring them to the type-level.
+
+Not all methods can be implemented in the type-level,
+or in the same way.
+
+For example, type-level does not support higher-level generics,
+i.e. it is not possible to pass in a generic type and "invoke" it.
+
+Therefore, methods like `map` and `reduce()` cannot be implemented generically.
+They have to be implemented separately for each specific use case,
+or with reduced capability.
+
+They are exposed under the `ArrayPlus` namespace,
+while some common ones are exposed at top-level.
+
+Here are the list of array methods and their corresponding type-level functions, if availableL
+
+- ✅ `at`: [`ArrayPlus.At`](#arrayplusat)
+- ✅ `concat`: [`Concat`](#arrayplusconcat)
+- 🚧 `copyWithin`: `CopyWithin`
+- 🚧 ⬇️ `entries`: `Array => Array<[number, T]>`, `Tuple<...T> => [[0, T1], [1, T2], ...[n, Tn]]`\
+ (this is not the same as `array.entries()` which returns `IterableIterator<[number, T]>`)
+- 🚧 `every`: `Every`
+- 🚧 `fill`: `Fill`
+- 🚧 `find`: `Find => V | never`
+- 🚧 `findIndex`: `FindIndex => number | number literal | never`
+- 🚧 `flat`: `Flat`
+- 🚧 `flatMap`: `Flat`
+- 🚧 `includes`:
+- 🚧 `join`:
+- 🚧 `keys`:
+- 🚧 `map`: `Map`
+- 🚧 `pop`: `Pop`
+- 🚧 `push`: `Push`
+- 🚧 `reduce`:
+- 🚧 `reduceRight`:
+- 🚧 `reverse`:
+- 🚧 `shift`:
+- 🚧 `slice`:
+- 🚧 `some`:
+- 🚧 `sort`:
+- 🚧 `splice`:
+- 🚧 `unshift`:
+- 🚧 `values`:
+
## References
- [handbook]
diff --git a/ts/index.ts b/ts/index.ts
index 12d589f88a..9791a9e509 100644
--- a/ts/index.ts
+++ b/ts/index.ts
@@ -1,7 +1,8 @@
export { required, requiredDeep, unpartial } from 'unpartial'
export type { IsAnyOrNever } from './any/any_or_never.js'
export type { AnyType, IsAny, IsNotAny, NotAnyType } from './any/any_type.js'
-export type { At, Concat } from './array/array.js'
+export type { At } from './array/array.at.js'
+export type { Concat } from './array/array.concat.js'
export * as ArrayPlus from './array/array_plus.js'
export type { ArrayType, IsArray, IsNotArray, NotArrayType } from './array/array_type.js'
export * from './array/index.js'
@@ -101,3 +102,4 @@ export type { UnionKeys } from './union_keys.js'
export type { IsNotUnknown, IsUnknown, NotUnknownType, UnknownType } from './unknown/unknown_type.js'
export * from './utils/index.js'
export type { IsNotVoid, IsVoid, NotVoidType, VoidType } from './void/void_type.js'
+