diff --git a/index.d.ts b/index.d.ts index 8a9926d8d..52cba3c70 100644 --- a/index.d.ts +++ b/index.d.ts @@ -144,6 +144,7 @@ export type {ArraySlice} from './source/array-slice.d.ts'; export type {ArraySplice} from './source/array-splice.d.ts'; export type {ArrayTail} from './source/array-tail.d.ts'; export type {ArrayElement} from './source/array-element.d.ts'; +export type {ArrayLength} from './source/array-length.d.ts'; export type {SetFieldType, SetFieldTypeOptions} from './source/set-field-type.d.ts'; export type {Paths, PathsOptions} from './source/paths.d.ts'; export type {AllUnionFields} from './source/all-union-fields.d.ts'; diff --git a/readme.md b/readme.md index 47e9fee30..e6e76141b 100644 --- a/readme.md +++ b/readme.md @@ -266,6 +266,7 @@ Click the type names for complete docs. - [`ExtractRestElement`](source/extract-rest-element.d.ts) - Extract the [`rest`](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) element type from an array. - [`ExcludeRestElement`](source/exclude-rest-element.d.ts) - Create a tuple with the [`rest`](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) element removed. - [`ArrayReverse`](source/array-reverse.d.ts) - Reverse the order of elements in a tuple type. +- [`ArrayLength`](source/array-length.d.ts) - Return the length of an array. Equivalent to `T['length']` where `T` extends any array. ### Numeric diff --git a/source/array-length.d.ts b/source/array-length.d.ts new file mode 100644 index 000000000..97949e40b --- /dev/null +++ b/source/array-length.d.ts @@ -0,0 +1,36 @@ +/** +Return the length of an array. Equivalent to `T['length']` where `T` extends any array. + +Tuples resolve to numeric literals, while non-tuples resolve to the `number` type. + +@example +``` +import type {ArrayLength} from 'type-fest'; + +type TupleLength = ArrayLength<[1, 2, 3]>; +//=> 3 + +type TupleWithOptionalMembersLength = ArrayLength<[1, 2, number?]>; +//=> 2 | 3 + +type NonTupleArrayLength = ArrayLength; +//=> number + +type TupleWithRestElementLength = ArrayLength<[1, 2, ...string[]]>; +//=> number + +// Distinguish between arrays with fixed and non-fixed lengths +type IsFixedLengthArray = number extends ArrayLength ? false : true; + +type A = IsFixedLengthArray; +//=> false + +type B = IsFixedLengthArray<[1, 2, 3]>; +//=> true +``` + +@category Array +*/ +export type ArrayLength = T['length']; + +export {}; diff --git a/source/internal/array.d.ts b/source/internal/array.d.ts index 66aa0d194..20a48555b 100644 --- a/source/internal/array.d.ts +++ b/source/internal/array.d.ts @@ -4,13 +4,6 @@ import type {OptionalKeysOf} from '../optional-keys-of.d.ts'; import type {UnknownArray} from '../unknown-array.d.ts'; import type {IsExactOptionalPropertyTypesEnabled, IfNotAnyOrNever} from './type.d.ts'; -/** -Infer the length of the given array ``. - -@link https://itnext.io/implementing-arithmetic-within-typescripts-type-system-a1ef140a6f6f -*/ -type ArrayLength = T extends {readonly length: infer L} ? L : never; - /** Matches any unknown array or tuple. */ diff --git a/test-d/array-length.ts b/test-d/array-length.ts new file mode 100644 index 000000000..45bb48adf --- /dev/null +++ b/test-d/array-length.ts @@ -0,0 +1,55 @@ +import {expectType} from 'tsd'; +import type {ArrayLength, Primitive} from '../index.d.ts'; + +// Non-tuples +expectType({} as ArrayLength); + +// Tuples +expectType<0>({} as ArrayLength<[]>); +expectType<1>({} as ArrayLength<[never]>); +expectType<3>({} as ArrayLength<['one', 2, true]>); +expectType<2 | 3 | 4>({} as ArrayLength<[number, string, boolean?, boolean?]>); + +expectType({} as ArrayLength<[1, 2, ...unknown[]]>); +expectType({} as ArrayLength<[1, 2?, ...unknown[]]>); +expectType({} as ArrayLength<[...unknown[], 1, 2]>); +expectType({} as ArrayLength<[0, ...unknown[], 1, 2]>); + +// Read-only arrays +expectType({} as ArrayLength); +expectType({} as ArrayLength); +expectType<0>({} as ArrayLength); +expectType<1>({} as ArrayLength); +expectType<3>({} as ArrayLength); +expectType<2 | 3 | 4>({} as ArrayLength); + +expectType({} as ArrayLength); +expectType({} as ArrayLength); +expectType({} as ArrayLength); +expectType({} as ArrayLength); + +// Unions +expectType<0 | 2>({} as ArrayLength<[] | [1, 2]>); +expectType<0 | 2>({} as ArrayLength); +expectType<0 | 2>({} as ArrayLength<[] | readonly [1, 2]>); +expectType<1 | 2 | 3 | 4>({} as ArrayLength<[1, 2?, 3?] | ['one', 'two', 'three', 'four']>); +expectType({} as ArrayLength); + +// Edge cases and disallowed types +expectType({} as ArrayLength); +expectType({} as ArrayLength); + +// @ts-expect-error +type DisallowedPrimitive = ArrayLength; +// @ts-expect-error +type DisallowedPrimitives = ArrayLength; +// @ts-expect-error +type DisallowedObject = ArrayLength<{}>; +// @ts-expect-error +type DisallowedMap = ArrayLength>; +// @ts-expect-error +type DisallowedSet = ArrayLength>; +// @ts-expect-error +type DisallowedRecord = ArrayLength>; +// @ts-expect-error +type DisallowedObjectWithLength = ArrayLength<{length: number}>;