diff --git a/source/merge-deep.d.ts b/source/merge-deep.d.ts index f1ceaca69..70e4aaac6 100644 --- a/source/merge-deep.d.ts +++ b/source/merge-deep.d.ts @@ -79,11 +79,16 @@ Pick the rest type. @example ``` -type Rest1 = PickRestType<[]>; // => [] -type Rest2 = PickRestType<[string]>; // => [] -type Rest3 = PickRestType<[...number[]]>; // => number[] -type Rest4 = PickRestType<[string, ...number[]]>; // => number[] -type Rest5 = PickRestType; // => string[] +type Rest1 = PickRestType<[]>; +//=> [] +type Rest2 = PickRestType<[string]>; +//=> [] +type Rest3 = PickRestType<[...number[]]>; +//=> number[] +type Rest4 = PickRestType<[string, ...number[]]>; +//=> number[] +type Rest5 = PickRestType; +//=> string[] ``` */ type PickRestType = number extends Type['length'] @@ -104,12 +109,18 @@ Omit the rest type. @example ``` -type Tuple1 = OmitRestType<[]>; // => [] -type Tuple2 = OmitRestType<[string]>; // => [string] -type Tuple3 = OmitRestType<[...number[]]>; // => [] -type Tuple4 = OmitRestType<[string, ...number[]]>; // => [string] -type Tuple5 = OmitRestType<[string, boolean[], ...number[]]>; // => [string, boolean[]] -type Tuple6 = OmitRestType; // => [] +type Tuple1 = OmitRestType<[]>; +//=> [] +type Tuple2 = OmitRestType<[string]>; +//=> [string] +type Tuple3 = OmitRestType<[...number[]]>; +//=> [] +type Tuple4 = OmitRestType<[string, ...number[]]>; +//=> [string] +type Tuple5 = OmitRestType<[string, boolean[], ...number[]]>; +//=> [string, boolean[]] +type Tuple6 = OmitRestType; +//=> [] ``` */ type OmitRestType = number extends Type['length'] @@ -238,9 +249,13 @@ type DoMergeArrayOrTuple< Destination extends UnknownArrayOrTuple, Source extends UnknownArrayOrTuple, Options extends MergeDeepInternalOptions, -> = ShouldSpread extends true - ? Array[number] | Exclude[number]> - : Source; // 'replace' +> = [Destination, Source] extends [readonly [], readonly []] + ? Source extends [] + ? [] + : readonly [] + : ShouldSpread extends true + ? Array[number] | Exclude[number]> + : Source; // 'replace' /** Merge two arrays recursively. @@ -270,18 +285,24 @@ Merge two array/tuple recursively by selecting one of the four strategies accord - tuple/array - array/tuple - array/array + +Each cases are considered that the one or both are empty. */ type MergeDeepArrayOrTupleRecursive< Destination extends UnknownArrayOrTuple, Source extends UnknownArrayOrTuple, Options extends MergeDeepInternalOptions, -> = IsBothExtends extends true - ? MergeDeepTupleAndTupleRecursive - : Destination extends NonEmptyTuple - ? MergeDeepTupleAndArrayRecursive - : Source extends NonEmptyTuple - ? MergeDeepArrayAndTupleRecursive - : MergeDeepArrayRecursive; +> = Destination extends [] + ? Source + : Source extends [] + ? Destination + : IsBothExtends extends true + ? MergeDeepTupleAndTupleRecursive + : Destination extends NonEmptyTuple + ? MergeDeepTupleAndArrayRecursive + : Source extends NonEmptyTuple + ? MergeDeepArrayAndTupleRecursive + : MergeDeepArrayRecursive; /** Merge two array/tuple according to {@link MergeDeepOptions.recurseIntoArrays recurseIntoArrays} option. @@ -405,7 +426,7 @@ type Bar = { }; type FooBar1 = MergeDeep; -// { +// => { // life: number; // name: string; // items: number[]; @@ -413,7 +434,7 @@ type FooBar1 = MergeDeep; // } type FooBar2 = MergeDeep; -// { +// => { // life: number; // name: string; // items: (string | number)[]; @@ -426,16 +447,20 @@ type FooBar2 = MergeDeep; import type {MergeDeep} from 'type-fest'; // Merge two arrays -type ArrayMerge = MergeDeep; // => (string | number)[] +type ArrayMerge = MergeDeep; +//=> (string | number)[] // Merge two tuples -type TupleMerge = MergeDeep<[1, 2, 3], ['a', 'b']>; // => (1 | 2 | 3 | 'a' | 'b')[] +type TupleMerge = MergeDeep<[1, 2, 3], ['a', 'b']>; +//=> (1 | 2 | 3 | 'a' | 'b')[] // Merge an array into a tuple -type TupleArrayMerge = MergeDeep<[1, 2, 3], string[]>; // => (string | 1 | 2 | 3)[] +type TupleArrayMerge = MergeDeep<[1, 2, 3], string[]>; +//=> (string | 1 | 2 | 3)[] // Merge a tuple into an array -type ArrayTupleMerge = MergeDeep; // => (number | 'b' | 'a')[] +type ArrayTupleMerge = MergeDeep; +//=> (number | 'b' | 'a')[] ``` @example diff --git a/test-d/merge-deep.ts b/test-d/merge-deep.ts index 979fa7f23..f8357b2b4 100644 --- a/test-d/merge-deep.ts +++ b/test-d/merge-deep.ts @@ -15,10 +15,10 @@ expectType<{}>(mergeDeep({}, {} as const)); expectType<{}>(mergeDeep({} as const, {} as const)); // Test valid signatures for arrays/tuples. -expectType(mergeDeep([], [])); -expectType(mergeDeep([] as const, [])); -expectType(mergeDeep([], [] as const)); -expectType(mergeDeep([] as const, [] as const)); +expectType<[]>(mergeDeep([], [])); +expectType<[]>(mergeDeep([] as const, [])); +expectType(mergeDeep([], [] as const)); +expectType(mergeDeep([] as const, [] as const)); // Test invalid signatures. expectType(mergeDeep({}, [])); @@ -56,7 +56,7 @@ expectType>(mergeDeep(['life'], [42] as const, {arrayMergeMod expectType>(mergeDeep(['life'] as const, [42] as const, {arrayMergeMode: 'replace'})); // Should merge tuples with union -expectType>(mergeDeep(['life', true], [42], {arrayMergeMode: 'spread'})); +expectType>(mergeDeep(['life', true], [42], {arrayMergeMode: 'spread'})); expectType>(mergeDeep(['life'], [42, true], {arrayMergeMode: 'spread'})); // Should not deep merge classes @@ -325,3 +325,64 @@ type OptionalWithUndefined = {a: string | undefined; b?: number; c?: boolean | u expectType({} as MergeDeep); expectType({} as MergeDeep); + +// Test for https://github.com/sindresorhus/type-fest/issues/1200 +type EmptyFoo = {foo: []}; +type NotEmptyTupleFoo = {foo: [0, 1]}; +type NotEmptyArrayFoo = {foo: number[]}; +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); + +// Test for https://github.com/sindresorhus/type-fest/issues/1200 +type EmptyReadonlyFoo = {readonly foo: []}; +type NotEmptyReadonlyTupleFoo = {readonly foo: [0, 1]}; +type NotEmptyReadonlyArrayFoo = {readonly foo: number[]}; +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); + +// Test for https://github.com/sindresorhus/type-fest/issues/1200 +type EmptyOptionalFoo = {foo?: []}; +type NotEmptyOptionalTupleFoo = {foo?: [0, 1]}; +type NotEmptyOptionalArrayFoo = {foo?: number[]}; +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); + +// Test for https://github.com/sindresorhus/type-fest/issues/1200 +type EmptyOptionalReadonlyFoo = {readonly foo?: []}; +type NotEmptyOptionalReadonlyTupleFoo = {readonly foo?: [0, 1]}; +type NotEmptyOptionalReadonlyArrayFoo = {readonly foo?: number[]}; +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); + +// Test for https://github.com/sindresorhus/type-fest/issues/1200 +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); +expectType({} as MergeDeep); + +// Test for https://github.com/sindresorhus/type-fest/issues/1200 +type EmptyFooRecursive = {foo: [0, string, {bar: []}]}; +type NotEmptyTupleFooRecursive = {foo: [0, number, {bar: [0, 1]}]}; +type NotEmptyArrayFooRecursive = {foo: [0, number, {bar: number[]}]}; +expectType({} as MergeDeep); +expectType<{foo: [0, string, {bar: [0, 1]}]}>({} as MergeDeep); +expectType({} as MergeDeep); +expectType<{foo: [0, string, {bar: number[]}]}>({} as MergeDeep); + +// Tuple and array recursion behavior with primitive values +expectType<[string, string, ...string[]]>({} as MergeDeep<[1, 2], string[], {recurseIntoArrays: true; arrayMergeMode: 'replace'}>); +expectType<[string, string, ...string[]]>({} as MergeDeep<[1, 2], string[], {recurseIntoArrays: true; arrayMergeMode: 'spread'}>); +expectType<[1, 2, ...string[]]>({} as MergeDeep); +expectType<[1, 2, ...string[]]>({} as MergeDeep);