From 96acab152b9eb991bfd7c768ba60f8c4ed512758 Mon Sep 17 00:00:00 2001 From: skarab42 Date: Wed, 17 Aug 2022 13:48:12 +0200 Subject: [PATCH] `Spread`: Support arrays/tuples (#435) --- readme.md | 2 +- source/spread.d.ts | 41 ++++++++++++++++++++++++++++++++++++----- test-d/spread.ts | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 14532ca12..d1dc3c453 100644 --- a/readme.md +++ b/readme.md @@ -162,7 +162,7 @@ Click the type names for complete docs. - [`HasOptionalKeys`](source/has-optional-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any optional fields. - [`RequiredKeysOf`](source/required-keys-of.d.ts) - Extract all required keys from the given type. - [`HasRequiredKeys`](source/has-required-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any required fields. -- [`Spread`](source/spread.d.ts) - Mimic the type inferred by TypeScript when merging two objects using the spread operator. +- [`Spread`](source/spread.d.ts) - Mimic the type inferred by TypeScript when merging two objects or two arrays/tuples using the spread syntax. ### JSON diff --git a/source/spread.d.ts b/source/spread.d.ts index 395263653..25e8ddafb 100644 --- a/source/spread.d.ts +++ b/source/spread.d.ts @@ -2,7 +2,7 @@ import type {Except} from './except'; import type {RequiredKeysOf} from './required-keys-of'; import type {Simplify} from './simplify'; -type Spread_ = { +type SpreadObject = { [Key in keyof FirstType]: Key extends keyof SecondType ? FirstType[Key] | Required[Key] : FirstType[Key]; @@ -11,8 +11,17 @@ type Spread_ = { RequiredKeysOf | Exclude >; +type TupleOrArray = readonly [...unknown[]]; + +type SpreadTupleOrArray< + FirstType extends TupleOrArray, + SecondType extends TupleOrArray, +> = Array; + +type Spreadable = object | TupleOrArray; + /** -Mimic the type inferred by TypeScript when merging two objects using the spread operator. +Mimic the type inferred by TypeScript when merging two objects or two arrays/tuples using the spread syntax. @example ``` @@ -46,9 +55,31 @@ const baz = (argument: FooBar) => { baz(fooBar); ``` +@example +``` +import type {Spread} from 'type-fest'; + +const foo = [1, 2, 3]; +const bar = ['4', '5', '6']; + +const fooBar = [...foo, ...bar]; +type FooBar = Spread; +// FooBar = (string | number)[] + +const baz = (argument: FooBar) => { + // Do something +}; + +baz(fooBar); +``` + @category Object */ export type Spread< - FirstType extends object, - SecondType extends object, -> = Simplify>; + FirstType extends Spreadable, + SecondType extends Spreadable, +> = FirstType extends TupleOrArray + ? SecondType extends TupleOrArray + ? SpreadTupleOrArray + : Simplify> + : Simplify>; diff --git a/test-d/spread.ts b/test-d/spread.ts index 48ae8ba1a..ea274acc1 100644 --- a/test-d/spread.ts +++ b/test-d/spread.ts @@ -42,3 +42,41 @@ const bar: Bar = { const foobar = {...foo, ...bar}; expectType(foobar); + +const arrayFoo = [1, 2, 3]; +const arrayBar = [4, 5, 6]; + +const arrayFooBar = [...arrayFoo, ...arrayBar]; //=> number[] +type ArrayFooBar = Spread; + +expectType(arrayFooBar); +expectType(arrayFooBar); + +const stringArray = ['4', '5', '6']; + +const mixedArrayFooBar = [...arrayFoo, ...stringArray]; //=> (string | number)[] +type MixedArrayFooBar = Spread; + +expectType(mixedArrayFooBar); +expectType>(mixedArrayFooBar); + +const tupleFoo: [1, 2, 3] = [1, 2, 3]; +const tupleBar: [4, 5, 6] = [4, 5, 6]; + +const tupleFooBar = [...tupleFoo, ...tupleBar]; //=> (1 | 2 | 3 | 4 | 5 | 6)[] +type TupleFooBar = Spread; + +expectType(tupleFooBar); +expectType>(tupleFooBar); + +const arrayTupleFooBar = [...arrayFoo, ...tupleBar]; //=> number[] +type ArrayTupleFooBar = Spread; + +expectType(arrayTupleFooBar); +expectType(arrayTupleFooBar); + +const tupleArrayFooBar = [...tupleFoo, ...arrayBar]; //=> number[] +type TupleArrayFooBar = Spread; + +expectType(tupleArrayFooBar); +expectType(tupleArrayFooBar);