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' +