From 56e83c14f85ce57dfdc46c8c028aa6c6b0fc8587 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Tue, 25 Mar 2025 10:39:27 +0800 Subject: [PATCH 01/16] Add ArrayFlat type --- index.d.ts | 1 + readme.md | 1 + source/array-flat.d.ts | 56 +++++++++++++++++++++++++++++++++++++++ test-d/array-flat.ts | 60 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 source/array-flat.d.ts create mode 100644 test-d/array-flat.ts diff --git a/index.d.ts b/index.d.ts index c8c021f36..ad70c4179 100644 --- a/index.d.ts +++ b/index.d.ts @@ -129,6 +129,7 @@ export type {ArrayValues} from './source/array-values'; export type {ArraySlice} from './source/array-slice'; export type {ArraySplice} from './source/array-splice'; export type {ArrayTail} from './source/array-tail'; +export type {ArrayFlat} from './source/array-flat'; export type {SetFieldType} from './source/set-field-type'; export type {Paths} from './source/paths'; export type {AllUnionFields} from './source/all-union-fields'; diff --git a/readme.md b/readme.md index e74c9572e..ca68a29ff 100644 --- a/readme.md +++ b/readme.md @@ -285,6 +285,7 @@ type ShouldBeNever = IfAny<'not any', 'not never', 'never'>; - [`Includes`](source/includes.d.ts) - Returns a boolean for whether the given array includes the given item. - [`Join`](source/join.d.ts) - Join an array of strings and/or numbers using the given string as a delimiter. - [`ArraySlice`](source/array-slice.d.ts) - Returns an array slice of a given range, just like `Array#slice()`. +- [`ArrayFlat`](source/array-flat.d.ts) - Creates a new array type by flattening an array to a specified depth, just like `Array#flat()`. - [`LastArrayElement`](source/last-array-element.d.ts) - Extracts the type of the last element of an array. - [`FixedLengthArray`](source/fixed-length-array.d.ts) - Create a type that represents an array of the given type and length. - [`MultidimensionalArray`](source/multidimensional-array.d.ts) - Create a type that represents a multidimensional array of the given type and dimensions. diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts new file mode 100644 index 000000000..760c2339b --- /dev/null +++ b/source/array-flat.d.ts @@ -0,0 +1,56 @@ +import type {IsAny} from './is-any'; +import type {IsNever} from './is-never'; +import type {Or} from './or'; +import type {Subtract} from './subtract'; +import type {UnknownArray} from './unknown-array'; + +/** +Creates a new array type by flattening an array to a specified depth. + +Use-case: Flatten an array type to a specified depth. + +Like [`Array#flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) but for types. + +@example +``` +import type {ArrayFlat, PositiveInfinity} from 'type-fest'; + +type FlatArr0 = ArrayFlat<[[0, 1], [2, 3], [4, 5]]>; +//=> type FlatArr0 = [0, 1, 2, 3, 4, 5]; + +// Flatten to depth +type Arr1 = [[0, [1, [2, [3, [4, [5]]]]]]]; +type FlatArr1 = ArrayFlat; +//=> type FlatArr1 = [0, [1, [2, [3, [4, [5]]]]]]; + +type FlatArr2 = ArrayFlat; +//=> type FlatArr2 = [0, 1, 2, [3, [4, [5]]]]; + +// Flatten to depth Infinity +type FlatArr3 = ArrayFlat; +//=> type FlatArr3 = [0, 1, 2, 3, 4, 5]; +``` + +@category Array +*/ +export type ArrayFlat = InternalArrayFlat; + +// Internal implementation +type InternalArrayFlat = +T extends UnknownArray + ? T['length'] extends 0 + ? [...Result, ...T] + : Depth extends 0 + ? [...Result, ...T] + : T extends readonly [infer ArrayItem, ...infer Last] + ? ArrayItem extends UnknownArray + ? InternalArrayFlat>]> + : InternalArrayFlat + : T extends Array + ? Or, IsNever> extends true + ? [...Result, ...ArrayItem2[]] // Return never/any[] when input is never/any[] + : ArrayItem2 extends UnknownArray + ? InternalArrayFlat, Result> + : [...Result, ...ArrayItem2[]] + : [...Result, ...T] + : T; diff --git a/test-d/array-flat.ts b/test-d/array-flat.ts new file mode 100644 index 000000000..ef3342fc9 --- /dev/null +++ b/test-d/array-flat.ts @@ -0,0 +1,60 @@ +import {expectType} from 'tsd'; +import type {ArrayFlat, PositiveInfinity} from '../index'; + +// Basic flattening tests +expectType>([]); +expectType>([1, 2, 3]); +expectType>([1, 2, 3, 4]); +expectType>([1, 2, 3, 4]); +expectType>([1, 2, [3], 4]); + +// Test with explicit depth +// eslint-disable-next-line unicorn/prevent-abbreviations +type Arr = [[0, [1, [2, [3, [4, [5]]]]]]]; +expectType>(null! as Arr); +// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments +expectType>([0, [1, [2, [3, [4, [5]]]]]]); +expectType>([0, 1, [2, [3, [4, [5]]]]]); +expectType>([0, 1, 2, [3, [4, [5]]]]); +expectType>([0, 1, 2, 3, [4, [5]]]); +expectType>([0, 1, 2, 3, 4, [5]]); +expectType>([0, 1, 2, 3, 4, 5]); +expectType>([0, 1, 2, 3, 4, 5]); +expectType>([0, 1, 2, 3, 4, 5]); + +// Test with Infinity depth +expectType>([1, 2, 3, 4]); +expectType>([1]); + +// Test with different element types +expectType>([null! as string, null! as number, null! as boolean]); +expectType>([null! as {a: number}, null! as {b: string}]); + +// Test with union types +expectType>([1, 2] as [1, 2] | [3, 4]); +expectType>([1, 2] as [1, 2] | [3, 4]); + +// Test with rest elements +expectType]>>([null! as number, null! as string]); +expectType]>>([1, 2, null! as string, null! as boolean]); +expectType], 4]>>([1, 2, ...(null! as Array<3>), 4]); + +// Test with mixed arrays and tuples +expectType>([1, ...(null! as number[]), 3]); +expectType>([null! as string, null! as number, ...(null! as boolean[])]); + +// Test with deeply nested structures +expectType>([1, 2, 3, 4, [5]]); + +// Test with readonly arrays +expectType>([1, 2, 3]); +expectType>([1, 2, 3, 4]); + +// Edge cases +expectType>>([]); +expectType>([]); +expectType>([]); +expectType>(null! as undefined[]); +expectType>(null! as any[]); +expectType>(null! as unknown[]); +expectType>(null! as never[]); From 904caec8ec490d9e8fe7ffabe8ffad9791337a88 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Thu, 3 Apr 2025 10:29:48 +0800 Subject: [PATCH 02/16] fix: support non-fixed array --- source/array-flat.d.ts | 77 ++++++++++++++++++++++++++++++++++-------- test-d/array-flat.ts | 53 +++++++++++++++++++++++++---- 2 files changed, 110 insertions(+), 20 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 760c2339b..f58b0ce16 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -4,6 +4,39 @@ import type {Or} from './or'; import type {Subtract} from './subtract'; import type {UnknownArray} from './unknown-array'; +/** + * Builds an array by repeating the given array items the specified number of times. + * + * @example + * ``` + * type FlatArr0 = BuildRepeatedArray<[number, string], 3>; + * //=> type FlatArr0 = [number, string, number, string, number, string]; + * ``` + */ +type BuildRepeatedArray = + N extends 0 + ? R + : BuildRepeatedArray, [...T, ...R]>; + +type ArrayFlatOptions = { + /** + * The number of times to repeat the array items when flattening an non-fixed length array. + * + * @example + * ``` + * type FlatArr0 = ArrayFlat, 1, { repeat: 3 }>; + * //=> type FlatArr0 = [number?, string?, number?, string?, number?, string?]; + * ``` + * + * @default 10 + */ + repeat: number; +}; + +type ArrayFlatDefaultOptions = { + repeat: 10; +}; + /** Creates a new array type by flattening an array to a specified depth. @@ -33,24 +66,40 @@ type FlatArr3 = ArrayFlat; @category Array */ -export type ArrayFlat = InternalArrayFlat; +export type ArrayFlat = +InternalArrayFlat; // Internal implementation -type InternalArrayFlat = +type InternalArrayFlat = T extends UnknownArray ? T['length'] extends 0 ? [...Result, ...T] : Depth extends 0 ? [...Result, ...T] - : T extends readonly [infer ArrayItem, ...infer Last] - ? ArrayItem extends UnknownArray - ? InternalArrayFlat>]> - : InternalArrayFlat - : T extends Array - ? Or, IsNever> extends true - ? [...Result, ...ArrayItem2[]] // Return never/any[] when input is never/any[] - : ArrayItem2 extends UnknownArray - ? InternalArrayFlat, Result> - : [...Result, ...ArrayItem2[]] - : [...Result, ...T] - : T; + : number extends T['length'] + ? [ + ...Result, + ...( + T[number] extends UnknownArray + ? BuildRepeatedArray, + Subtract, + Options + >, + Options['repeat'] + > + : T + ), + ] + : T extends readonly [infer ArrayItem, ...infer Last] + ? ArrayItem extends UnknownArray + ? InternalArrayFlat, Options>]> + : InternalArrayFlat + : T extends Array + ? Or, IsNever> extends true + ? [...Result, ...ArrayItem2[]] // Return never/any[] when input is never/any[] + : ArrayItem2 extends UnknownArray + ? InternalArrayFlat, Options, Result> + : [...Result, ...ArrayItem2[]] + : [...Result, ...T] + : []; diff --git a/test-d/array-flat.ts b/test-d/array-flat.ts index ef3342fc9..cb0868694 100644 --- a/test-d/array-flat.ts +++ b/test-d/array-flat.ts @@ -1,4 +1,4 @@ -import {expectType} from 'tsd'; +import {expectAssignable, expectType} from 'tsd'; import type {ArrayFlat, PositiveInfinity} from '../index'; // Basic flattening tests @@ -34,9 +34,7 @@ expectType>([null! as {a: number}, null! expectType>([1, 2] as [1, 2] | [3, 4]); expectType>([1, 2] as [1, 2] | [3, 4]); -// Test with rest elements -expectType]>>([null! as number, null! as string]); -expectType]>>([1, 2, null! as string, null! as boolean]); +expectType]>>(null! as [number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?]); expectType], 4]>>([1, 2, ...(null! as Array<3>), 4]); // Test with mixed arrays and tuples @@ -55,6 +53,49 @@ expectType>>([]); expectType>([]); expectType>([]); expectType>(null! as undefined[]); -expectType>(null! as any[]); expectType>(null! as unknown[]); -expectType>(null! as never[]); + +// Test specifically for non-tuple array handling (Array vs [T, T]) +type GenericNumberArrays = number[][]; +expectAssignable>([1, 2, 3, 4, 5]); + +// Test for deeply nested non-tuple arrays with specific depths +type DeepGenericArray = number[][][][]; +expectAssignable>([[1, 2], [3, 4]]); +expectAssignable>([1, 2, 3, 4]); + +// Test for mixed generic arrays and tuples +type MixedGenericAndTuple = Array<[number, string]>; +expectAssignable>([1, 'a', 2, 'b']); + +// Test for array with optional elements in nested structure +type NestedOptional = [number, Array<[string?, number?]>]; +expectAssignable>([1, 'a', 2, 'b', 3]); + +// Test for arrays with rest elements in nested structure +type NestedRest = [string, Array<[...number[]]>]; +expectAssignable>(['a', 1, 2, 3, 4, 5]); + +// Test for flattening arrays with union types in nested structures +type NestedUnion = Array>; +expectAssignable>(['a', 1, 'b', 2]); + +// Test for empty array in complex structure +type ComplexWithEmpty = [number, Array<[]>, string]; +expectAssignable>([1, 'string']); + +// Test for array with undefined/null elements +type ArrayWithNullish = [number, [undefined, null]]; +expectAssignable>([1, undefined, null]); + +// Test for array with mixed depth elements +type MixedDepthArray = [number, string[], [[boolean]]]; +expectAssignable>([1, 'a', 'b', [true]]); + +// Test for readonly nested arrays with different depths +type ReadonlyNestedComplex = readonly [number, ReadonlyArray]; +expectAssignable>([1, 'a', 'b', 'c']); + +// Test for recursive flattening with non-array elements +type RecursiveWithNonArray = [number, [string, {a: number}]]; +expectAssignable>([1, 'string', {a: 42}]); From 6ef28bac85fd8fe31b772d89e134cac7abc3dbea Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Thu, 3 Apr 2025 10:39:21 +0800 Subject: [PATCH 03/16] fix: fix ts infinite error --- source/array-flat.d.ts | 16 ++++++++++------ type-fest@4.37.0 | 0 2 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 type-fest@4.37.0 diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index f58b0ce16..04fe1f078 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -13,10 +13,10 @@ import type {UnknownArray} from './unknown-array'; * //=> type FlatArr0 = [number, string, number, string, number, string]; * ``` */ -type BuildRepeatedArray = +type BuildRepeatedArray = N extends 0 ? R - : BuildRepeatedArray, [...T, ...R]>; + : BuildRepeatedArray, [...R, ...CopyT]>; type ArrayFlatOptions = { /** @@ -77,20 +77,24 @@ T extends UnknownArray : Depth extends 0 ? [...Result, ...T] : number extends T['length'] + // Handle non-fixed length arrays ? [ ...Result, ...( T[number] extends UnknownArray - ? BuildRepeatedArray, Subtract, Options - >, - Options['repeat'] - > + > extends infer Item + ? Item extends UnknownArray + ? BuildRepeatedArray + : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. + : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. : T ), ] + // Handle fixed length arrays : T extends readonly [infer ArrayItem, ...infer Last] ? ArrayItem extends UnknownArray ? InternalArrayFlat, Options>]> diff --git a/type-fest@4.37.0 b/type-fest@4.37.0 new file mode 100644 index 000000000..e69de29bb From 9dab11862725c6ed04a17dfc7172a78eb43c3a4f Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Mon, 7 Apr 2025 10:38:40 +0800 Subject: [PATCH 04/16] rm file --- type-fest@4.37.0 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 type-fest@4.37.0 diff --git a/type-fest@4.37.0 b/type-fest@4.37.0 deleted file mode 100644 index e69de29bb..000000000 From 7d010974b744082571063de7ab964e8644ed9285 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Wed, 30 Apr 2025 17:20:55 +0800 Subject: [PATCH 05/16] enhance arrayflat for spread array --- source/array-flat.d.ts | 257 +++++++++++++++++++++++++++++-------- source/internal/array.d.ts | 70 ++++++++++ source/internal/type.d.ts | 5 + test-d/array-flat.ts | 55 +++++++- 4 files changed, 325 insertions(+), 62 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 04fe1f078..2b365acea 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -1,40 +1,41 @@ -import type {IsAny} from './is-any'; -import type {IsNever} from './is-never'; +import type {And} from './and'; +import type {ArrayLength, ExactOptionalPropertyTypesEnable, IsLeadingSpreadArray, IsTrailingSpreadArray, Not, OptionalPartOfArray, RequiredPartOfArray, StaticPartOfArray, StaticPartOfLeadingSpreadArray, VariablePartOfArray, VariablePartOfLeadingSpreadArray} from './internal'; +import type {IsEqual} from './is-equal'; import type {Or} from './or'; import type {Subtract} from './subtract'; import type {UnknownArray} from './unknown-array'; +declare const RepeatSymbol: unique symbol; + /** - * Builds an array by repeating the given array items the specified number of times. - * - * @example - * ``` - * type FlatArr0 = BuildRepeatedArray<[number, string], 3>; - * //=> type FlatArr0 = [number, string, number, string, number, string]; - * ``` - */ -type BuildRepeatedArray = - N extends 0 - ? R - : BuildRepeatedArray, [...R, ...CopyT]>; +Return true if the number is 0 +*/ +type IsZero = [T] extends [0] ? true : false; +/** + * Options for the `ArrayFlat` type. + */ type ArrayFlatOptions = { /** * The number of times to repeat the array items when flattening an non-fixed length array. * * @example * ``` - * type FlatArr0 = ArrayFlat, 1, { repeat: 3 }>; - * //=> type FlatArr0 = [number?, string?, number?, string?, number?, string?]; + * type FlatArr0 = ArrayFlat, 1, { maxRepeat: 3 }>; + * //=> type FlatArr0 = + * [] + * | [number, string] + * | [number, string, number, string] + * | [number, string, number, string, number, string]; * ``` * - * @default 10 + * @default 5 */ - repeat: number; + maxRepeat: number; }; -type ArrayFlatDefaultOptions = { - repeat: 10; +type DefaultArrayFlatOptions = { + maxRepeat: 5; }; /** @@ -66,44 +67,190 @@ type FlatArr3 = ArrayFlat; @category Array */ -export type ArrayFlat = -InternalArrayFlat; +export type ArrayFlat = +DoRepeatArrayItem, Options['maxRepeat']>; // Internal implementation -type InternalArrayFlat = +type InternalArrayFlat< + T, + Depth extends number = 1, + Options extends ArrayFlatOptions = DefaultArrayFlatOptions, + Result extends UnknownArray = [], +> = +T extends UnknownArray + ? Or>, IsZero> extends true + ? [...Result, ...T] + : number extends T['length'] + // Handle non-fixed length arrays + ? InternalNonFixedLengthArrayFlat + // Handle fixed length arrays + : InternalFixedLengthArrayFlat + : []; + +// Handle non-fixed length arrays +type InternalNonFixedLengthArrayFlat< + T, + Depth extends number = 1, + Options extends ArrayFlatOptions = DefaultArrayFlatOptions, + Result extends UnknownArray = [], +> = +T extends UnknownArray + ? Or>, IsZero> extends true + ? [...Result, ...T] + : IsTrailingSpreadArray extends true + // Handle trailing spread array + ? [StaticPartOfArray, VariablePartOfArray] extends [infer StaticPart, infer VariablePart] + ? InternalFixedLengthArrayFlat extends infer StaticPartResult extends UnknownArray + ? [StaticPartResult, InternalNonFixedLengthArrayFlat] extends + [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] + ? [...Result, ...Result1, ...Result2] + : never + : never + : never // Never happens + // Handle leading spread array + : IsLeadingSpreadArray extends true + ? [VariablePartOfLeadingSpreadArray, StaticPartOfLeadingSpreadArray] extends [infer VariablePart, infer StaticPart] + ? [InternalNonFixedLengthArrayFlat, InternalFixedLengthArrayFlat] extends + [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] + ? [...Result, ...Result1, ...Result2] + : never + : never // Never happens + // Handle non-spread and non-fixed-length array + : [ + T[number] extends UnknownArray + ? InternalArrayFlat, Options, Result> + : [T[number]], + ] extends [infer Item extends UnknownArray] + ? Item extends [{[RepeatSymbol]: unknown}] + ? Item + : [{[RepeatSymbol]: Item}] + : never // Never happens + : T; + +// Handle fixed length arrays +type InternalFixedLengthArrayFlat< + T, + Depth extends number = 1, + Options extends ArrayFlatOptions = DefaultArrayFlatOptions, + Result extends UnknownArray = [], +> = T extends UnknownArray - ? T['length'] extends 0 + ? Or>, IsZero> extends true ? [...Result, ...T] - : Depth extends 0 - ? [...Result, ...T] - : number extends T['length'] - // Handle non-fixed length arrays - ? [ - ...Result, - ...( - T[number] extends UnknownArray - ? InternalArrayFlat< - number extends T[number]['length'] ? T[number] : Partial, - Subtract, - Options - > extends infer Item - ? Item extends UnknownArray - ? BuildRepeatedArray + : T extends readonly [infer ArrayItem, ...infer Last] + ? [ArrayItem] extends [UnknownArray] + ? number extends ArrayLength + ? InternalNonFixedLengthArrayFlat extends infer Item extends UnknownArray + ? InternalFixedLengthArrayFlat + : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. + : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart, infer OptionalPart] + ? InternalArrayFlat< + Last, + Depth, + Options, + [InternalArrayFlat, Options>, (InternalArrayFlat, Options> | [])] extends + [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] + ? [...Result, ...Result1, ...Result2] + : never + > + : never // Never happens + : InternalInnerFixedLengthArrayFlat + : [...Result, ...T] + : []; + +// Handle fixed length arrays +type InternalInnerFixedLengthArrayFlat< + T, + Depth extends number = 1, + Options extends ArrayFlatOptions = DefaultArrayFlatOptions, + Result extends UnknownArray = [], +> = +[T] extends [UnknownArray] + ? Or>, IsZero> extends true + ? [...Result, ...T] + : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart, infer OptionalPart] + ? [InternalArrayFlat, (InternalArrayFlat | [])] extends + [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] + ? [...Result, ...Result1, ...Result2] + : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. + : never // Never happens + : []; + +/** + * Replaces items with the RepeatSymbol flag to the true result. + */ +type DoRepeatArrayItem = +T extends [infer _Item, ...infer Last] + ? [_Item] extends [{[RepeatSymbol]: infer Item extends UnknownArray}] + ? IsZero extends true + ? [...DoRepeatArrayItem] + : Item extends unknown + ? Item['length'] extends 1 + // If the item is a single element array, we can build [...Array], but if already has spread + // array before, we should build [...Array<'SomeSpreadArrayBefore'>, Item[number], Item[number], Item[number], ...] + ? [ + ...( + hasSpreadArray extends true + ? BuildRepeatedUnionArray extends infer Result extends UnknownArray + ? [...Result] : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. + : Array + ) + , ...DoRepeatArrayItem, + ] + // If the item is not a single element array, we only can build by repeating the item, like: + // ArrayFlat> => [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, ...] + : [ + ...( + BuildRepeatedUnionArray> extends infer Result extends UnknownArray + ? [...Result] : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. - : T - ), - ] - // Handle fixed length arrays - : T extends readonly [infer ArrayItem, ...infer Last] - ? ArrayItem extends UnknownArray - ? InternalArrayFlat, Options>]> - : InternalArrayFlat - : T extends Array - ? Or, IsNever> extends true - ? [...Result, ...ArrayItem2[]] // Return never/any[] when input is never/any[] - : ArrayItem2 extends UnknownArray - ? InternalArrayFlat, Options, Result> - : [...Result, ...ArrayItem2[]] - : [...Result, ...T] - : []; + ) + , ...DoRepeatArrayItem, // eslint-disable-line @typescript-eslint/no-unnecessary-type-arguments + ] + : never // Never happens + : [_Item, ...DoRepeatArrayItem] + : T; + +/** + * Builds a union that lists all the possible combinations of the given array items and repeat times. + * + * @example + * ``` + * type A = BuildRepeatedUnionArray<[number, string?], 2, true>; + * //=> type A = + * [] + * | number[] + * | [number] + * | [number, string] + * | [number, number] + * | [number, string, number] + * | [number, number, string] + * | [number, string, number, string] + * ``` + */ +type BuildRepeatedUnionArray = +RepeatNumber extends 0 + ? R + : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart extends UnknownArray, infer OptionalPart extends UnknownArray] + ? ExactOptionalPropertyTypesEnable extends true + ? R + | [...RequiredPart] + | (And, CanSpread> extends true + ? [...Array] + : never) + | BuildRepeatedUnionArray< + T, + Subtract, + CanSpread, + [ + ...R, + ...( + ExactOptionalPropertyTypesEnable extends true + ? [...RequiredPart, ...([Exclude] | [])] + : T + ), + ] + > + : never + : never; diff --git a/source/internal/array.d.ts b/source/internal/array.d.ts index 6f72db229..055dfc547 100644 --- a/source/internal/array.d.ts +++ b/source/internal/array.d.ts @@ -30,6 +30,36 @@ It creates a type-safe way to access the element type of `unknown` type. */ export type ArrayElement = T extends readonly unknown[] ? T[0] : never; +/** + Returns the required part of the given array. + + @example + ``` + type A = [string, number, boolean?]; + type B = RequiredPartOfArray; + //=> [string, number] + ``` + */ +export type RequiredPartOfArray = + T extends readonly [infer U, ...infer V] + ? [U, ...RequiredPartOfArray] + : []; + +/** + Returns the optional part of the given array. + + @example + ``` + type A = [string, number, boolean?]; + type B = OptionalPartOfArray; + //=> [boolean?] + ``` + */ +export type OptionalPartOfArray = + T extends readonly [...RequiredPartOfArray, ...infer U] + ? U + : []; + /** Returns the static, fixed-length portion of the given array, excluding variable-length parts. @@ -66,6 +96,46 @@ export type VariablePartOfArray = : [] : never; // Should never happen +/** +Returns if the given array is a leading spread array. +*/ +export type IsLeadingSpreadArray = + T extends [...infer U, infer V] ? true : false; + +/** +Returns if the given array is a trailing spread array. +*/ +export type IsTrailingSpreadArray = + T extends [infer U, ...infer V] ? true : false; + +/** +Returns the static, fixed-length portion of the given leading spread array. +@example +``` +type A = [...string[], number, boolean]; +type B = StaticPartOfLeadingSpreadArray; +//=> [number, boolean] +``` +*/ +type StaticPartOfLeadingSpreadArray = + T extends [...infer U, infer V] + ? StaticPartOfLeadingSpreadArray + : Result; + +/** +Returns the variable, non-fixed-length portion of the given leading spread array. +@example +``` +type A = [...string[], number, boolean]; +type B = VariablePartOfLeadingSpreadArray; +//=> string[] +``` +*/ +export type VariablePartOfLeadingSpreadArray = + T extends [...infer U, ...StaticPartOfLeadingSpreadArray] + ? U + : never; + /** Set the given array to readonly if `IsReadonly` is `true`, otherwise set the given array to normal, then return the result. diff --git a/source/internal/type.d.ts b/source/internal/type.d.ts index 46c0343b4..d4d9ff586 100644 --- a/source/internal/type.d.ts +++ b/source/internal/type.d.ts @@ -111,3 +111,8 @@ type InternalIsUnion = ? boolean extends Result ? true : Result : never; // Should never happen + +/** +Return the value of exactOptionalPropertyTypes option in tsconfig +*/ +export type ExactOptionalPropertyTypesEnable = [(string | undefined)?] extends [string?] ? false : true; diff --git a/test-d/array-flat.ts b/test-d/array-flat.ts index cb0868694..99b6ca5e8 100644 --- a/test-d/array-flat.ts +++ b/test-d/array-flat.ts @@ -1,6 +1,8 @@ -import {expectAssignable, expectType} from 'tsd'; +import {expectAssignable, expectNotAssignable, expectType} from 'tsd'; import type {ArrayFlat, PositiveInfinity} from '../index'; +type DeepArrayFlat = ArrayFlat<[[[[[[T]]]]]], 10>; + // Basic flattening tests expectType>([]); expectType>([1, 2, 3]); @@ -26,20 +28,56 @@ expectType>([0, 1, 2, 3, 4, 5]); expectType>([1, 2, 3, 4]); expectType>([1]); +expectAssignable>( + [true, 'a', false], +); +expectAssignable>( + [true, 'a', 1, false], +); + // Test with different element types expectType>([null! as string, null! as number, null! as boolean]); +expectType>([null! as string, null! as number, null! as boolean]); + expectType>([null! as {a: number}, null! as {b: string}]); +expectType>([null! as {a: number}, null! as {b: string}]); // Test with union types -expectType>([1, 2] as [1, 2] | [3, 4]); -expectType>([1, 2] as [1, 2] | [3, 4]); +expectType>(null! as [1, 2] | [3, 4]); +expectType>(null! as [1, 2] | [3, 4]); + +expectType>(null! as [1, 2] | [3, 4]); +expectType>(null! as [1, 2] | [3, 4]); + +expectAssignable]>>([1, 3, 5]); +expectAssignable]>>([1, 3, 5]); -expectType]>>(null! as [number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?, number?, string?]); -expectType], 4]>>([1, 2, ...(null! as Array<3>), 4]); +expectType], 4]>>(null! as [1, 2, ...Array<3>, 4]); +expectType], 4]>>(null! as [1, 2, ...Array<3>, 4]); // Test with mixed arrays and tuples -expectType>([1, ...(null! as number[]), 3]); -expectType>([null! as string, null! as number, ...(null! as boolean[])]); +expectType>(null! as ['1', ...number[], '3']); +expectType>(null! as ['1', ...number[], '3']); +expectAssignable>([1, 2, 3, 'a', 'b', 'c']); +expectAssignable>([1, 2, 3, 'a', 'b', 'c']); +type MutiSpreadArray = [number, Array<'a'>, Array<'b'>, boolean]; +expectAssignable>([1, 'a', 'b', true]); +expectAssignable>([1, 'a', 'b', true]); +expectAssignable>([1, 'a', 'a', 'a', 'b', true]); +expectAssignable>([1, 'a', 'a', 'a', 'b', true]); + +type SpreadArray = ArrayFlat<[1, ...Array<[2, 3?]>, 4]>; +expectAssignable([1, 2, 3, 4]); +expectAssignable([1, 2, 2, 4]); +expectAssignable([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4]); +expectAssignable([1, 2, 3, 2, 4]); + +type SpreadArray2 = ArrayFlat<['start', ...Array<[2, 3, 4?]>, 'end']>; +expectAssignable(['start', 2, 3, 4, 'end']); +expectAssignable(['start', 2, 3, 4, 2, 3, 4, 'end']); +expectAssignable(['start', 2, 3, 4, 2, 3, 4, 2, 3, 4, 'end']); +expectAssignable(['start', 2, 3, 2, 3, 2, 3, 'end']); +expectAssignable(['start', 2, 3, 2, 3, 4, 'end']); // Test with deeply nested structures expectType>([1, 2, 3, 4, [5]]); @@ -71,6 +109,9 @@ expectAssignable>([1, 'a', 2, 'b']); // Test for array with optional elements in nested structure type NestedOptional = [number, Array<[string?, number?]>]; expectAssignable>([1, 'a', 2, 'b', 3]); +expectType>( + null! as [boolean, string, boolean] | [boolean, string, number | undefined, boolean], +); // Test for arrays with rest elements in nested structure type NestedRest = [string, Array<[...number[]]>]; From 2ea01566980ee54eb4f141d9b438f51b20c3abc8 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Wed, 30 Apr 2025 17:30:40 +0800 Subject: [PATCH 06/16] add more test --- test-d/array-flat.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test-d/array-flat.ts b/test-d/array-flat.ts index 99b6ca5e8..e299d6ac2 100644 --- a/test-d/array-flat.ts +++ b/test-d/array-flat.ts @@ -3,6 +3,11 @@ import type {ArrayFlat, PositiveInfinity} from '../index'; type DeepArrayFlat = ArrayFlat<[[[[[[T]]]]]], 10>; +expectAssignable>([1, 2, 3, 'a', 'b', true]); +expectAssignable>([1, 2, 3, 'a', 'b', true]); +// TODO: fix this +// expectAssignable>(['a', true, 'b', 'c']); + // Basic flattening tests expectType>([]); expectType>([1, 2, 3]); From 6902b80c1fdbeafcbb348ddb3fa09257bc6f72ae Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Wed, 30 Apr 2025 17:57:49 +0800 Subject: [PATCH 07/16] fix array item repeat --- source/array-flat.d.ts | 4 ++-- test-d/array-flat.ts | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 2b365acea..cceb622f7 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -191,7 +191,7 @@ T extends [infer _Item, ...infer Last] ? [ ...( hasSpreadArray extends true - ? BuildRepeatedUnionArray extends infer Result extends UnknownArray + ? BuildRepeatedUnionArray> extends infer Result extends UnknownArray ? [...Result] : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. : Array @@ -247,7 +247,7 @@ RepeatNumber extends 0 ...R, ...( ExactOptionalPropertyTypesEnable extends true - ? [...RequiredPart, ...([Exclude] | [])] + ? [...RequiredPart, ...(IsZero> extends true ? [] : [Exclude] | [])] : T ), ] diff --git a/test-d/array-flat.ts b/test-d/array-flat.ts index e299d6ac2..d8d2bf415 100644 --- a/test-d/array-flat.ts +++ b/test-d/array-flat.ts @@ -5,8 +5,7 @@ type DeepArrayFlat = ArrayFlat<[[[[[[T]]]]]], 10>; expectAssignable>([1, 2, 3, 'a', 'b', true]); expectAssignable>([1, 2, 3, 'a', 'b', true]); -// TODO: fix this -// expectAssignable>(['a', true, 'b', 'c']); +expectAssignable>(['a', true, true, false, 'b']); // Basic flattening tests expectType>([]); From 7288dac99a9bec812112d625f7c4d4c39cb26a52 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 8 May 2025 18:49:37 +0700 Subject: [PATCH 08/16] Update array-flat.d.ts --- source/array-flat.d.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 7a51f0248..63f7533ae 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -13,8 +13,8 @@ Return true if the number is 0 type IsZero = [T] extends [0] ? true : false; /** - * Options for the `ArrayFlat` type. - */ +Options for the `ArrayFlat` type. +*/ type ArrayFlatOptions = { /** * The number of times to repeat the array items when flattening an non-fixed length array. @@ -81,7 +81,7 @@ T extends UnknownArray ? Or>, IsZero> extends true ? [...Result, ...T] : number extends T['length'] - // Handle non-fixed length arrays + // Handle non-fixed length arrays ? InternalNonFixedLengthArrayFlat // Handle fixed length arrays : InternalFixedLengthArrayFlat @@ -186,7 +186,7 @@ T extends [infer _Item, ...infer Last] ? [...DoRepeatArrayItem] : Item extends unknown ? Item['length'] extends 1 - // If the item is a single element array, we can build [...Array], but if already has spread + // If the item is a single element array, we can build [...Array], but if it already has spread // array before, we should build [...Array<'SomeSpreadArrayBefore'>, Item[number], Item[number], Item[number], ...] ? [ ...( @@ -198,8 +198,8 @@ T extends [infer _Item, ...infer Last] ) , ...DoRepeatArrayItem, ] - // If the item is not a single element array, we only can build by repeating the item, like: - // ArrayFlat> => [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, ...] + // If the item is not a single element array, we can only build by repeating the item, like: + // ArrayFlat> => [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, ...] : [ ...( BuildRepeatedUnionArray> extends infer Result extends UnknownArray From 190685e404f6821bbe5940caddbb108d09d77c99 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 8 May 2025 18:51:37 +0700 Subject: [PATCH 09/16] Update array-flat.d.ts --- source/array-flat.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 63f7533ae..b45d7a824 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -8,7 +8,7 @@ import type {UnknownArray} from './unknown-array.d.ts'; declare const RepeatSymbol: unique symbol; /** -Return true if the number is 0 +Return true if the number is 0. */ type IsZero = [T] extends [0] ? true : false; @@ -17,7 +17,7 @@ Options for the `ArrayFlat` type. */ type ArrayFlatOptions = { /** - * The number of times to repeat the array items when flattening an non-fixed length array. + * The number of times to repeat the array items when flattening a non-fixed length array. * * @example * ``` From d3b6ddb1e0886cf11923ffcee9f7d69bb0018ec1 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Thu, 15 May 2025 10:57:34 +0800 Subject: [PATCH 10/16] fix cr --- source/array-flat.d.ts | 68 ++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index b45d7a824..5e3134721 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -17,20 +17,22 @@ Options for the `ArrayFlat` type. */ type ArrayFlatOptions = { /** - * The number of times to repeat the array items when flattening a non-fixed length array. - * - * @example - * ``` - * type FlatArr0 = ArrayFlat, 1, { maxRepeat: 3 }>; - * //=> type FlatArr0 = - * [] - * | [number, string] - * | [number, string, number, string] - * | [number, string, number, string, number, string]; - * ``` - * - * @default 5 - */ + The number of times to repeat the array items when flattening a non-fixed length array. + + @default 5 + + @example + ``` + import type {ArrayFlat} from 'type-fest'; + + type FlatArr0 = ArrayFlat, 1, { maxRepeat: 3 }>; + //=> type FlatArr0 = + [] + | [number, string] + | [number, string, number, string] + | [number, string, number, string, number, string]; + ``` + */ maxRepeat: number; }; @@ -41,7 +43,7 @@ type DefaultArrayFlatOptions = { /** Creates a new array type by flattening an array to a specified depth. -Use-case: Flatten an array type to a specified depth. +Use-case: You can handle data nested at any level within arrays without worrying about the nesting structure Like [`Array#flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) but for types. @@ -177,8 +179,8 @@ type InternalInnerFixedLengthArrayFlat< : []; /** - * Replaces items with the RepeatSymbol flag to the true result. - */ +Replaces items with the RepeatSymbol flag to the true result. +*/ type DoRepeatArrayItem = T extends [infer _Item, ...infer Last] ? [_Item] extends [{[RepeatSymbol]: infer Item extends UnknownArray}] @@ -213,22 +215,22 @@ T extends [infer _Item, ...infer Last] : T; /** - * Builds a union that lists all the possible combinations of the given array items and repeat times. - * - * @example - * ``` - * type A = BuildRepeatedUnionArray<[number, string?], 2, true>; - * //=> type A = - * [] - * | number[] - * | [number] - * | [number, string] - * | [number, number] - * | [number, string, number] - * | [number, number, string] - * | [number, string, number, string] - * ``` - */ +Builds a union that lists all the possible combinations of the given array items and repeat times. + +@example +``` +type A = BuildRepeatedUnionArray<[number, string?], 2, true>; +//=> type A = +[] +| number[] +| [number] +| [number, string] +| [number, number] +| [number, string, number] +| [number, number, string] +| [number, string, number, string] +``` +*/ type BuildRepeatedUnionArray = RepeatNumber extends 0 ? R From 29c0ad85ef1381b534c33181aed6c9568b8aabc7 Mon Sep 17 00:00:00 2001 From: Haozheng Li Date: Thu, 15 May 2025 11:04:56 +0800 Subject: [PATCH 11/16] Update type.d.ts --- source/internal/type.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/internal/type.d.ts b/source/internal/type.d.ts index 4b5a4be32..ac4a43391 100644 --- a/source/internal/type.d.ts +++ b/source/internal/type.d.ts @@ -98,7 +98,7 @@ type C = IfNotAnyOrNever; ``` */ export type IfNotAnyOrNever = -If, IfAny, If, IfNever, IfNotAnyOrNever>>; + If, IfAny, If, IfNever, IfNotAnyOrNever>>; /** Return the value of exactOptionalPropertyTypes option in tsconfig From 266b1debd74ce4d5c4f7a6988d8f572368a55a53 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Thu, 15 May 2025 11:25:43 +0800 Subject: [PATCH 12/16] code improvements --- source/array-flat.d.ts | 80 ++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 5e3134721..74efcd49e 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -70,13 +70,12 @@ type FlatArr3 = ArrayFlat; @category Array */ export type ArrayFlat = -DoRepeatArrayItem, Options['maxRepeat']>; +DoRepeatArrayItem, Options['maxRepeat']>; // Internal implementation type InternalArrayFlat< T, Depth extends number = 1, - Options extends ArrayFlatOptions = DefaultArrayFlatOptions, Result extends UnknownArray = [], > = T extends UnknownArray @@ -84,16 +83,15 @@ T extends UnknownArray ? [...Result, ...T] : number extends T['length'] // Handle non-fixed length arrays - ? InternalNonFixedLengthArrayFlat + ? InternalNonFixedLengthArrayFlat // Handle fixed length arrays - : InternalFixedLengthArrayFlat + : InternalFixedLengthArrayFlat : []; // Handle non-fixed length arrays type InternalNonFixedLengthArrayFlat< T, Depth extends number = 1, - Options extends ArrayFlatOptions = DefaultArrayFlatOptions, Result extends UnknownArray = [], > = T extends UnknownArray @@ -101,26 +99,22 @@ T extends UnknownArray ? [...Result, ...T] : IsTrailingSpreadArray extends true // Handle trailing spread array - ? [StaticPartOfArray, VariablePartOfArray] extends [infer StaticPart, infer VariablePart] - ? InternalFixedLengthArrayFlat extends infer StaticPartResult extends UnknownArray - ? [StaticPartResult, InternalNonFixedLengthArrayFlat] extends - [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] - ? [...Result, ...Result1, ...Result2] - : never - : never - : never // Never happens + ? [ + ...Result, + ...InternalFixedLengthArrayFlat, Depth>, + ...InternalNonFixedLengthArrayFlat, Depth>, + ] // Handle leading spread array : IsLeadingSpreadArray extends true - ? [VariablePartOfLeadingSpreadArray, StaticPartOfLeadingSpreadArray] extends [infer VariablePart, infer StaticPart] - ? [InternalNonFixedLengthArrayFlat, InternalFixedLengthArrayFlat] extends - [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] - ? [...Result, ...Result1, ...Result2] - : never - : never // Never happens + ? [ + ...Result, + ...InternalNonFixedLengthArrayFlat, Depth>, + ...InternalFixedLengthArrayFlat, Depth>, + ] // Handle non-spread and non-fixed-length array : [ T[number] extends UnknownArray - ? InternalArrayFlat, Options, Result> + ? InternalArrayFlat, Result> : [T[number]], ] extends [infer Item extends UnknownArray] ? Item extends [{[RepeatSymbol]: unknown}] @@ -133,7 +127,6 @@ T extends UnknownArray type InternalFixedLengthArrayFlat< T, Depth extends number = 1, - Options extends ArrayFlatOptions = DefaultArrayFlatOptions, Result extends UnknownArray = [], > = T extends UnknownArray @@ -142,21 +135,17 @@ T extends UnknownArray : T extends readonly [infer ArrayItem, ...infer Last] ? [ArrayItem] extends [UnknownArray] ? number extends ArrayLength - ? InternalNonFixedLengthArrayFlat extends infer Item extends UnknownArray - ? InternalFixedLengthArrayFlat + ? InternalNonFixedLengthArrayFlat extends infer Item extends UnknownArray + ? InternalFixedLengthArrayFlat : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart, infer OptionalPart] ? InternalArrayFlat< Last, Depth, - Options, - [InternalArrayFlat, Options>, (InternalArrayFlat, Options> | [])] extends - [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] - ? [...Result, ...Result1, ...Result2] - : never + [...Result, ...InternalArrayFlat>, ...(InternalArrayFlat> | [])] > : never // Never happens - : InternalInnerFixedLengthArrayFlat + : InternalInnerFixedLengthArrayFlat : [...Result, ...T] : []; @@ -164,17 +153,13 @@ T extends UnknownArray type InternalInnerFixedLengthArrayFlat< T, Depth extends number = 1, - Options extends ArrayFlatOptions = DefaultArrayFlatOptions, Result extends UnknownArray = [], > = [T] extends [UnknownArray] ? Or>, IsZero> extends true ? [...Result, ...T] : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart, infer OptionalPart] - ? [InternalArrayFlat, (InternalArrayFlat | [])] extends - [infer Result1 extends UnknownArray, infer Result2 extends UnknownArray] - ? [...Result, ...Result1, ...Result2] - : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. + ? [...Result, ...InternalArrayFlat, ...(InternalArrayFlat | [])] : never // Never happens : []; @@ -185,17 +170,15 @@ type DoRepeatArrayItem extends true - ? [...DoRepeatArrayItem] + ? DoRepeatArrayItem : Item extends unknown ? Item['length'] extends 1 - // If the item is a single element array, we can build [...Array], but if it already has spread - // array before, we should build [...Array<'SomeSpreadArrayBefore'>, Item[number], Item[number], Item[number], ...] + // If the item is a single element array, we can build [...Array], but if it already has spread + // array before, we should build [...Array<'SomeSpreadArrayBefore'>, Item[number], Item[number], Item[number], ...] ? [ ...( hasSpreadArray extends true - ? BuildRepeatedUnionArray> extends infer Result extends UnknownArray - ? [...Result] - : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. + ? BuildRepeatedUnionArray> : Array ) , ...DoRepeatArrayItem, @@ -203,12 +186,8 @@ T extends [infer _Item, ...infer Last] // If the item is not a single element array, we can only build by repeating the item, like: // ArrayFlat> => [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, ...] : [ - ...( - BuildRepeatedUnionArray> extends infer Result extends UnknownArray - ? [...Result] - : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. - ) - , ...DoRepeatArrayItem, // eslint-disable-line @typescript-eslint/no-unnecessary-type-arguments + ...BuildRepeatedUnionArray>, + ...DoRepeatArrayItem, ] : never // Never happens : [_Item, ...DoRepeatArrayItem] @@ -237,9 +216,9 @@ RepeatNumber extends 0 : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart extends UnknownArray, infer OptionalPart extends UnknownArray] ? ExactOptionalPropertyTypesEnable extends true ? R - | [...RequiredPart] + | RequiredPart | (And, CanSpread> extends true - ? [...Array] + ? Array : never) | BuildRepeatedUnionArray< T, @@ -249,7 +228,10 @@ RepeatNumber extends 0 ...R, ...( ExactOptionalPropertyTypesEnable extends true - ? [...RequiredPart, ...(IsZero> extends true ? [] : [Exclude] | [])] + ? [ + ...RequiredPart, + ...(IsZero> extends true ? [] : [Exclude] | []), + ] : T ), ] From 44a99d62d2498b291473e7381388e55ccf1ac0c5 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Thu, 15 May 2025 11:42:01 +0800 Subject: [PATCH 13/16] code improvements --- source/array-flat.d.ts | 79 ++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 74efcd49e..3d2b39a56 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -135,16 +135,16 @@ T extends UnknownArray : T extends readonly [infer ArrayItem, ...infer Last] ? [ArrayItem] extends [UnknownArray] ? number extends ArrayLength - ? InternalNonFixedLengthArrayFlat extends infer Item extends UnknownArray - ? InternalFixedLengthArrayFlat - : never // Never happens, just for fixed ts error TS2589: Type instantiation is excessively deep and possibly infinite. - : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart, infer OptionalPart] - ? InternalArrayFlat< - Last, - Depth, - [...Result, ...InternalArrayFlat>, ...(InternalArrayFlat> | [])] - > - : never // Never happens + ? InternalFixedLengthArrayFlat]> + : InternalArrayFlat< + Last, + Depth, + [ + ...Result, + ...InternalArrayFlat, Subtract>, + ...(InternalArrayFlat, Subtract> | []), + ] + > : InternalInnerFixedLengthArrayFlat : [...Result, ...T] : []; @@ -158,9 +158,7 @@ type InternalInnerFixedLengthArrayFlat< [T] extends [UnknownArray] ? Or>, IsZero> extends true ? [...Result, ...T] - : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart, infer OptionalPart] - ? [...Result, ...InternalArrayFlat, ...(InternalArrayFlat | [])] - : never // Never happens + : [...Result, ...InternalArrayFlat, Depth>, ...(InternalArrayFlat< OptionalPartOfArray, Depth> | [])] : []; /** @@ -210,31 +208,36 @@ type A = BuildRepeatedUnionArray<[number, string?], 2, true>; | [number, string, number, string] ``` */ -type BuildRepeatedUnionArray = +type BuildRepeatedUnionArray< + T extends UnknownArray, + RepeatNumber extends number, + CanSpread extends boolean = false, + R extends unknown[] = [], + RequiredPart extends UnknownArray = RequiredPartOfArray, + OptionalPart extends UnknownArray = OptionalPartOfArray, +> = RepeatNumber extends 0 ? R - : [RequiredPartOfArray, OptionalPartOfArray] extends [infer RequiredPart extends UnknownArray, infer OptionalPart extends UnknownArray] - ? ExactOptionalPropertyTypesEnable extends true - ? R - | RequiredPart - | (And, CanSpread> extends true - ? Array - : never) - | BuildRepeatedUnionArray< - T, - Subtract, - CanSpread, - [ - ...R, - ...( - ExactOptionalPropertyTypesEnable extends true - ? [ - ...RequiredPart, - ...(IsZero> extends true ? [] : [Exclude] | []), - ] - : T - ), - ] - > - : never + : ExactOptionalPropertyTypesEnable extends true + ? R + | RequiredPart + | (And, CanSpread> extends true + ? Array + : never) + | BuildRepeatedUnionArray< + T, + Subtract, + CanSpread, + [ + ...R, + ...( + ExactOptionalPropertyTypesEnable extends true + ? [ + ...RequiredPart, + ...(IsZero> extends true ? [] : [Exclude] | []), + ] + : T + ), + ] + > : never; From 3d8b622d215d0df01968f80499405b9ed086d1a9 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Fri, 16 May 2025 13:45:05 +0800 Subject: [PATCH 14/16] add tests; fix bug --- source/array-flat.d.ts | 40 +++++++------ source/internal/array.d.ts | 63 ++++++++++++++++++--- test-d/internal/is-leading-spread-array.ts | 12 ++++ test-d/internal/is-middle-spread-array.ts | 15 +++++ test-d/internal/is-trailing-spread-array.ts | 12 ++++ 5 files changed, 118 insertions(+), 24 deletions(-) create mode 100644 test-d/internal/is-leading-spread-array.ts create mode 100644 test-d/internal/is-middle-spread-array.ts create mode 100644 test-d/internal/is-trailing-spread-array.ts diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 3d2b39a56..6a180b11e 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -1,5 +1,5 @@ import type {And} from './and.d.ts'; -import type {ArrayLength, ExactOptionalPropertyTypesEnable, IsLeadingSpreadArray, IsTrailingSpreadArray, Not, OptionalPartOfArray, RequiredPartOfArray, StaticPartOfArray, StaticPartOfLeadingSpreadArray, VariablePartOfArray, VariablePartOfLeadingSpreadArray} from './internal/index.d.ts'; +import type {ArrayLength, ExactOptionalPropertyTypesEnable, IsLeadingSpreadArray, IsMiddleSpreadArray, IsTrailingSpreadArray, LeadingStaticPartOfMiddleSpreadArray, Not, OptionalPartOfStaticArray, RequiredPartOfStaticArray, StaticPartOfArray, StaticPartOfLeadingSpreadArray, TrailingStaticPartOfMiddleSpreadArray, VariablePartOfArray, VariablePartOfLeadingSpreadArray, VariablePartOfMiddleSpreadArray} from './internal/index.d.ts'; import type {IsEqual} from './is-equal.d.ts'; import type {Or} from './or.d.ts'; import type {Subtract} from './subtract.d.ts'; @@ -111,16 +111,24 @@ T extends UnknownArray ...InternalNonFixedLengthArrayFlat, Depth>, ...InternalFixedLengthArrayFlat, Depth>, ] - // Handle non-spread and non-fixed-length array - : [ - T[number] extends UnknownArray - ? InternalArrayFlat, Result> - : [T[number]], - ] extends [infer Item extends UnknownArray] - ? Item extends [{[RepeatSymbol]: unknown}] - ? Item - : [{[RepeatSymbol]: Item}] - : never // Never happens + // Handle middle spread array + : IsMiddleSpreadArray extends true + ? [ + ...Result, + ...InternalFixedLengthArrayFlat, Depth>, + ...InternalNonFixedLengthArrayFlat, Depth>, + ...InternalFixedLengthArrayFlat, Depth>, + ] + // Handle non-spread and non-fixed-length array + : [ + T[number] extends UnknownArray + ? InternalArrayFlat, Result> + : [T[number]], + ] extends [infer Item extends UnknownArray] + ? Item extends [{[RepeatSymbol]: unknown}] + ? Item + : [{[RepeatSymbol]: Item}] + : never // Never happens : T; // Handle fixed length arrays @@ -141,8 +149,8 @@ T extends UnknownArray Depth, [ ...Result, - ...InternalArrayFlat, Subtract>, - ...(InternalArrayFlat, Subtract> | []), + ...InternalArrayFlat, Subtract>, + ...(InternalArrayFlat, Subtract> | []), ] > : InternalInnerFixedLengthArrayFlat @@ -158,7 +166,7 @@ type InternalInnerFixedLengthArrayFlat< [T] extends [UnknownArray] ? Or>, IsZero> extends true ? [...Result, ...T] - : [...Result, ...InternalArrayFlat, Depth>, ...(InternalArrayFlat< OptionalPartOfArray, Depth> | [])] + : [...Result, ...InternalArrayFlat, Depth>, ...(InternalArrayFlat< OptionalPartOfStaticArray, Depth> | [])] : []; /** @@ -213,8 +221,8 @@ type BuildRepeatedUnionArray< RepeatNumber extends number, CanSpread extends boolean = false, R extends unknown[] = [], - RequiredPart extends UnknownArray = RequiredPartOfArray, - OptionalPart extends UnknownArray = OptionalPartOfArray, + RequiredPart extends UnknownArray = RequiredPartOfStaticArray, + OptionalPart extends UnknownArray = OptionalPartOfStaticArray, > = RepeatNumber extends 0 ? R diff --git a/source/internal/array.d.ts b/source/internal/array.d.ts index 31b20c033..3e4ae7b1b 100644 --- a/source/internal/array.d.ts +++ b/source/internal/array.d.ts @@ -37,13 +37,13 @@ export type ArrayElement = T extends readonly unknown[] ? T[0] : never; @example ``` type A = [string, number, boolean?]; - type B = RequiredPartOfArray; + type B = RequiredPartOfStaticArray; //=> [string, number] ``` */ -export type RequiredPartOfArray = +export type RequiredPartOfStaticArray = T extends readonly [infer U, ...infer V] - ? [U, ...RequiredPartOfArray] + ? [U, ...RequiredPartOfStaticArray] : []; /** @@ -52,12 +52,12 @@ export type RequiredPartOfArray = @example ``` type A = [string, number, boolean?]; - type B = OptionalPartOfArray; + type B = OptionalPartOfStaticArray; //=> [boolean?] ``` */ -export type OptionalPartOfArray = - T extends readonly [...RequiredPartOfArray, ...infer U] +export type OptionalPartOfStaticArray = + T extends readonly [...RequiredPartOfStaticArray, ...infer U] ? U : []; @@ -101,13 +101,35 @@ export type VariablePartOfArray = Returns if the given array is a leading spread array. */ export type IsLeadingSpreadArray = - T extends [...infer U, infer V] ? true : false; +number extends ArrayLength + ? T extends [infer K, ...infer U, infer V] + ? false + : T extends [...infer U, infer V] + ? true + : false + : false; /** Returns if the given array is a trailing spread array. */ export type IsTrailingSpreadArray = - T extends [infer U, ...infer V] ? true : false; +number extends ArrayLength + ? T extends [infer K, ...infer U, infer V] + ? false + : T extends [infer U, ...infer V] + ? true + : false + : false; + +/** +Returns if the given array is a middle spread array. +*/ +export type IsMiddleSpreadArray = +number extends ArrayLength + ? T extends [infer K, ...infer U, infer V] + ? true + : false + : false; /** Returns the static, fixed-length portion of the given leading spread array. @@ -137,6 +159,31 @@ export type VariablePartOfLeadingSpreadArray = ? U : never; +/** +Returns the trailing static, fixed-length portion of the given middle spread array. +*/ +export type TrailingStaticPartOfMiddleSpreadArray = StaticPartOfLeadingSpreadArray; + +/** +Returns the leading variable, non-fixed-length portion of the given middle spread array. +*/ +export type LeadingStaticPartOfMiddleSpreadArray = + T extends [infer U, ...infer V] + ? LeadingStaticPartOfMiddleSpreadArray + : Result; + +/** +Returns the trailing variable, non-fixed-length portion of the given middle spread array. +*/ +export type VariablePartOfMiddleSpreadArray< + T extends UnknownArray, + LeadingStaticPart extends UnknownArray = LeadingStaticPartOfMiddleSpreadArray, + TrailingStaticPart extends UnknownArray = TrailingStaticPartOfMiddleSpreadArray, +> = + T extends [...LeadingStaticPart, ...infer U, ...TrailingStaticPart] + ? U + : never; + /** Set the given array to readonly if `IsReadonly` is `true`, otherwise set the given array to normal, then return the result. diff --git a/test-d/internal/is-leading-spread-array.ts b/test-d/internal/is-leading-spread-array.ts new file mode 100644 index 000000000..867650e3c --- /dev/null +++ b/test-d/internal/is-leading-spread-array.ts @@ -0,0 +1,12 @@ +import {expectType} from 'tsd'; +import type {IsLeadingSpreadArray} from '../../source/internal/array.js'; + +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); + +expectType>(true); +expectType>(true); +expectType>(true); diff --git a/test-d/internal/is-middle-spread-array.ts b/test-d/internal/is-middle-spread-array.ts new file mode 100644 index 000000000..29ae6993f --- /dev/null +++ b/test-d/internal/is-middle-spread-array.ts @@ -0,0 +1,15 @@ +import {expectType} from 'tsd'; +import type {IsMiddleSpreadArray} from '../../source/internal/array.js'; + +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); + +expectType>(true); +expectType>(true); +expectType>(true); + diff --git a/test-d/internal/is-trailing-spread-array.ts b/test-d/internal/is-trailing-spread-array.ts new file mode 100644 index 000000000..c574c6e5d --- /dev/null +++ b/test-d/internal/is-trailing-spread-array.ts @@ -0,0 +1,12 @@ +import {expectType} from 'tsd'; +import type {IsTrailingSpreadArray} from '../../source/internal/array.js'; + +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); + +expectType>(true); +expectType>(true); +expectType>(true); From c5336d14a8ae10c2979fb19b233230cb2bc4b737 Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Fri, 16 May 2025 13:49:30 +0800 Subject: [PATCH 15/16] add tests --- test-d/internal/is-leading-spread-array.ts | 2 ++ test-d/internal/is-middle-spread-array.ts | 2 ++ test-d/internal/is-trailing-spread-array.ts | 2 ++ 3 files changed, 6 insertions(+) diff --git a/test-d/internal/is-leading-spread-array.ts b/test-d/internal/is-leading-spread-array.ts index 867650e3c..f5ec88d03 100644 --- a/test-d/internal/is-leading-spread-array.ts +++ b/test-d/internal/is-leading-spread-array.ts @@ -2,6 +2,8 @@ import {expectType} from 'tsd'; import type {IsLeadingSpreadArray} from '../../source/internal/array.js'; expectType>(false); +expectType>(false); +expectType>(false); expectType>(false); expectType>(false); expectType>(false); diff --git a/test-d/internal/is-middle-spread-array.ts b/test-d/internal/is-middle-spread-array.ts index 29ae6993f..19f2b0c98 100644 --- a/test-d/internal/is-middle-spread-array.ts +++ b/test-d/internal/is-middle-spread-array.ts @@ -2,6 +2,8 @@ import {expectType} from 'tsd'; import type {IsMiddleSpreadArray} from '../../source/internal/array.js'; expectType>(false); +expectType>(false); +expectType>(false); expectType>(false); expectType>(false); expectType>(false); diff --git a/test-d/internal/is-trailing-spread-array.ts b/test-d/internal/is-trailing-spread-array.ts index c574c6e5d..916ee06f7 100644 --- a/test-d/internal/is-trailing-spread-array.ts +++ b/test-d/internal/is-trailing-spread-array.ts @@ -2,6 +2,8 @@ import {expectType} from 'tsd'; import type {IsTrailingSpreadArray} from '../../source/internal/array.js'; expectType>(false); +expectType>(false); +expectType>(false); expectType>(false); expectType>(false); expectType>(false); From a3677f18c5ef4e40df175708fed1ad83bacae69e Mon Sep 17 00:00:00 2001 From: emiyaaaaa Date: Fri, 16 May 2025 14:19:50 +0800 Subject: [PATCH 16/16] add tests --- source/array-flat.d.ts | 2 +- source/internal/array.d.ts | 99 +++++++++++-------- .../{ => array}/is-leading-spread-array.ts | 3 +- .../{ => array}/is-middle-spread-array.ts | 3 +- .../{ => array}/is-trailing-spread-array.ts | 3 +- ...ding-static-part-of-middle-spread-array.ts | 5 + .../array/optional-part-of-static-array.ts | 8 ++ .../array/required-part-of-static-array.ts | 8 ++ .../static-part-of-leading-spread-array.ts | 5 + ...ling-static-part-of-middle-spread-array.ts | 5 + .../variable-part-of-leading-spread-array.ts | 5 + .../variable-part-of-middle-spread-array.ts | 5 + 12 files changed, 108 insertions(+), 43 deletions(-) rename test-d/internal/{ => array}/is-leading-spread-array.ts (82%) rename test-d/internal/{ => array}/is-middle-spread-array.ts (85%) rename test-d/internal/{ => array}/is-trailing-spread-array.ts (82%) create mode 100644 test-d/internal/array/leading-static-part-of-middle-spread-array.ts create mode 100644 test-d/internal/array/optional-part-of-static-array.ts create mode 100644 test-d/internal/array/required-part-of-static-array.ts create mode 100644 test-d/internal/array/static-part-of-leading-spread-array.ts create mode 100644 test-d/internal/array/trailing-static-part-of-middle-spread-array.ts create mode 100644 test-d/internal/array/variable-part-of-leading-spread-array.ts create mode 100644 test-d/internal/array/variable-part-of-middle-spread-array.ts diff --git a/source/array-flat.d.ts b/source/array-flat.d.ts index 6a180b11e..1346a7409 100644 --- a/source/array-flat.d.ts +++ b/source/array-flat.d.ts @@ -173,7 +173,7 @@ type InternalInnerFixedLengthArrayFlat< Replaces items with the RepeatSymbol flag to the true result. */ type DoRepeatArrayItem = -T extends [infer _Item, ...infer Last] +T extends readonly [infer _Item, ...infer Last] ? [_Item] extends [{[RepeatSymbol]: infer Item extends UnknownArray}] ? IsZero extends true ? DoRepeatArrayItem diff --git a/source/internal/array.d.ts b/source/internal/array.d.ts index 3e4ae7b1b..8e6564be8 100644 --- a/source/internal/array.d.ts +++ b/source/internal/array.d.ts @@ -31,6 +31,61 @@ It creates a type-safe way to access the element type of `unknown` type. */ export type ArrayElement = T extends readonly unknown[] ? T[0] : never; +/** +Returns if the given array is a leading spread array. + +@example +``` +type A = [...string[], number, boolean]; +type B = IsLeadingSpreadArray; +//=> true +``` +*/ +export type IsLeadingSpreadArray = +number extends ArrayLength + ? T extends readonly [infer K, ...infer U, infer V] + ? false + : T extends readonly [...infer U, infer V] + ? true + : false + : false; + +/** +Returns if the given array is a trailing spread array. + +@example +``` +type A = [1, ...string[]]; +type B = IsTrailingSpreadArray; +//=> true +``` +*/ +export type IsTrailingSpreadArray = +number extends ArrayLength + ? T extends readonly [infer K, ...infer U, infer V] + ? false + : T extends readonly [infer U, ...infer V] + ? true + : false + : false; + +/** +Returns if the given array is a middle spread array. + +@example +``` +type A = [1, ...string[], 3]; +type B = IsMiddleSpreadArray; +//=> true +``` +*/ +export type IsMiddleSpreadArray = +number extends ArrayLength + ? T extends readonly [infer K, ...infer U, infer V] + ? true + : false + : false; + /** Returns the required part of the given array. @@ -97,40 +152,6 @@ export type VariablePartOfArray = : [] : never; // Should never happen -/** -Returns if the given array is a leading spread array. -*/ -export type IsLeadingSpreadArray = -number extends ArrayLength - ? T extends [infer K, ...infer U, infer V] - ? false - : T extends [...infer U, infer V] - ? true - : false - : false; - -/** -Returns if the given array is a trailing spread array. -*/ -export type IsTrailingSpreadArray = -number extends ArrayLength - ? T extends [infer K, ...infer U, infer V] - ? false - : T extends [infer U, ...infer V] - ? true - : false - : false; - -/** -Returns if the given array is a middle spread array. -*/ -export type IsMiddleSpreadArray = -number extends ArrayLength - ? T extends [infer K, ...infer U, infer V] - ? true - : false - : false; - /** Returns the static, fixed-length portion of the given leading spread array. @example @@ -140,8 +161,8 @@ type B = StaticPartOfLeadingSpreadArray; //=> [number, boolean] ``` */ -type StaticPartOfLeadingSpreadArray = - T extends [...infer U, infer V] +export type StaticPartOfLeadingSpreadArray = + T extends readonly [...infer U, infer V] ? StaticPartOfLeadingSpreadArray : Result; @@ -155,7 +176,7 @@ type B = VariablePartOfLeadingSpreadArray; ``` */ export type VariablePartOfLeadingSpreadArray = - T extends [...infer U, ...StaticPartOfLeadingSpreadArray] + T extends readonly [...infer U, ...StaticPartOfLeadingSpreadArray] ? U : never; @@ -168,7 +189,7 @@ export type TrailingStaticPartOfMiddleSpreadArray = Stat Returns the leading variable, non-fixed-length portion of the given middle spread array. */ export type LeadingStaticPartOfMiddleSpreadArray = - T extends [infer U, ...infer V] + T extends readonly [infer U, ...infer V] ? LeadingStaticPartOfMiddleSpreadArray : Result; @@ -180,7 +201,7 @@ export type VariablePartOfMiddleSpreadArray< LeadingStaticPart extends UnknownArray = LeadingStaticPartOfMiddleSpreadArray, TrailingStaticPart extends UnknownArray = TrailingStaticPartOfMiddleSpreadArray, > = - T extends [...LeadingStaticPart, ...infer U, ...TrailingStaticPart] + T extends readonly [...LeadingStaticPart, ...infer U, ...TrailingStaticPart] ? U : never; diff --git a/test-d/internal/is-leading-spread-array.ts b/test-d/internal/array/is-leading-spread-array.ts similarity index 82% rename from test-d/internal/is-leading-spread-array.ts rename to test-d/internal/array/is-leading-spread-array.ts index f5ec88d03..9f977bf89 100644 --- a/test-d/internal/is-leading-spread-array.ts +++ b/test-d/internal/array/is-leading-spread-array.ts @@ -1,5 +1,5 @@ import {expectType} from 'tsd'; -import type {IsLeadingSpreadArray} from '../../source/internal/array.js'; +import type {IsLeadingSpreadArray} from '../../../source/internal/array.js'; expectType>(false); expectType>(false); @@ -8,6 +8,7 @@ expectType>(false); expectType>(false); expectType>(false); expectType>(false); +expectType>(false); expectType>(true); expectType>(true); diff --git a/test-d/internal/is-middle-spread-array.ts b/test-d/internal/array/is-middle-spread-array.ts similarity index 85% rename from test-d/internal/is-middle-spread-array.ts rename to test-d/internal/array/is-middle-spread-array.ts index 19f2b0c98..92f9544ad 100644 --- a/test-d/internal/is-middle-spread-array.ts +++ b/test-d/internal/array/is-middle-spread-array.ts @@ -1,5 +1,5 @@ import {expectType} from 'tsd'; -import type {IsMiddleSpreadArray} from '../../source/internal/array.js'; +import type {IsMiddleSpreadArray} from '../../../source/internal/array.js'; expectType>(false); expectType>(false); @@ -10,6 +10,7 @@ expectType>(false); expectType>(false); expectType>(false); expectType>(false); +expectType>(false); expectType>(true); expectType>(true); diff --git a/test-d/internal/is-trailing-spread-array.ts b/test-d/internal/array/is-trailing-spread-array.ts similarity index 82% rename from test-d/internal/is-trailing-spread-array.ts rename to test-d/internal/array/is-trailing-spread-array.ts index 916ee06f7..e0e5e63d4 100644 --- a/test-d/internal/is-trailing-spread-array.ts +++ b/test-d/internal/array/is-trailing-spread-array.ts @@ -1,5 +1,5 @@ import {expectType} from 'tsd'; -import type {IsTrailingSpreadArray} from '../../source/internal/array.js'; +import type {IsTrailingSpreadArray} from '../../../source/internal/array.js'; expectType>(false); expectType>(false); @@ -8,6 +8,7 @@ expectType>(false); expectType>(false); expectType>(false); expectType>(false); +expectType>(false); expectType>(true); expectType>(true); diff --git a/test-d/internal/array/leading-static-part-of-middle-spread-array.ts b/test-d/internal/array/leading-static-part-of-middle-spread-array.ts new file mode 100644 index 000000000..b6ea807fb --- /dev/null +++ b/test-d/internal/array/leading-static-part-of-middle-spread-array.ts @@ -0,0 +1,5 @@ +import {expectType} from 'tsd'; +import type {LeadingStaticPartOfMiddleSpreadArray} from '../../../source/internal/array.js'; + +expectType>(null! as [1, 2]); +expectType>(null! as [1, 2]); diff --git a/test-d/internal/array/optional-part-of-static-array.ts b/test-d/internal/array/optional-part-of-static-array.ts new file mode 100644 index 000000000..2d43fd1a4 --- /dev/null +++ b/test-d/internal/array/optional-part-of-static-array.ts @@ -0,0 +1,8 @@ +import {expectType} from 'tsd'; +import type {OptionalPartOfStaticArray} from '../../../source/internal/array.js'; + +expectType>(null! as [4?]); +expectType>(null! as [4?]); +expectType>(null! as [4?, 5?]); +expectType>(null! as [4?, 5?, 6?]); +expectType>(null! as []); diff --git a/test-d/internal/array/required-part-of-static-array.ts b/test-d/internal/array/required-part-of-static-array.ts new file mode 100644 index 000000000..638fd235f --- /dev/null +++ b/test-d/internal/array/required-part-of-static-array.ts @@ -0,0 +1,8 @@ +import {expectType} from 'tsd'; +import type {RequiredPartOfStaticArray} from '../../../source/internal/array.js'; + +expectType>(null! as [1, 2, 3]); +expectType>(null! as [1, 2, 3]); +expectType>(null! as [1, 2, 3]); +expectType>(null! as []); + diff --git a/test-d/internal/array/static-part-of-leading-spread-array.ts b/test-d/internal/array/static-part-of-leading-spread-array.ts new file mode 100644 index 000000000..33466dda5 --- /dev/null +++ b/test-d/internal/array/static-part-of-leading-spread-array.ts @@ -0,0 +1,5 @@ +import {expectType} from 'tsd'; +import type {StaticPartOfLeadingSpreadArray} from '../../../source/internal/array.js'; + +expectType>(null! as [1, 2, 3]); +expectType>(null! as [1, 2, 3]); diff --git a/test-d/internal/array/trailing-static-part-of-middle-spread-array.ts b/test-d/internal/array/trailing-static-part-of-middle-spread-array.ts new file mode 100644 index 000000000..f3da9fd06 --- /dev/null +++ b/test-d/internal/array/trailing-static-part-of-middle-spread-array.ts @@ -0,0 +1,5 @@ +import {expectType} from 'tsd'; +import type {TrailingStaticPartOfMiddleSpreadArray} from '../../../source/internal/array.js'; + +expectType>(null! as [2, 3]); +expectType>(null! as [2, 3]); diff --git a/test-d/internal/array/variable-part-of-leading-spread-array.ts b/test-d/internal/array/variable-part-of-leading-spread-array.ts new file mode 100644 index 000000000..09bc0c94c --- /dev/null +++ b/test-d/internal/array/variable-part-of-leading-spread-array.ts @@ -0,0 +1,5 @@ +import {expectType} from 'tsd'; +import type {VariablePartOfLeadingSpreadArray} from '../../../source/internal/array.js'; + +expectType>(null! as string[]); +expectType>(null! as string[]); diff --git a/test-d/internal/array/variable-part-of-middle-spread-array.ts b/test-d/internal/array/variable-part-of-middle-spread-array.ts new file mode 100644 index 000000000..d4eeade08 --- /dev/null +++ b/test-d/internal/array/variable-part-of-middle-spread-array.ts @@ -0,0 +1,5 @@ +import {expectType} from 'tsd'; +import type {VariablePartOfMiddleSpreadArray} from '../../../source/internal/array.js'; + +expectType>(null! as string[]); +expectType>(null! as string[]);