Skip to content

Commit

Permalink
Spread: Support arrays/tuples (#435)
Browse files Browse the repository at this point in the history
  • Loading branch information
skarab42 committed Aug 17, 2022
1 parent 96bf69d commit 96acab1
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 6 deletions.
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -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

Expand Down
41 changes: 36 additions & 5 deletions source/spread.d.ts
Expand Up @@ -2,7 +2,7 @@ import type {Except} from './except';
import type {RequiredKeysOf} from './required-keys-of';
import type {Simplify} from './simplify';

type Spread_<FirstType extends object, SecondType extends object> = {
type SpreadObject<FirstType extends object, SecondType extends object> = {
[Key in keyof FirstType]: Key extends keyof SecondType
? FirstType[Key] | Required<SecondType>[Key]
: FirstType[Key];
Expand All @@ -11,8 +11,17 @@ type Spread_<FirstType extends object, SecondType extends object> = {
RequiredKeysOf<SecondType> | Exclude<keyof SecondType, keyof FirstType>
>;

type TupleOrArray = readonly [...unknown[]];

type SpreadTupleOrArray<
FirstType extends TupleOrArray,
SecondType extends TupleOrArray,
> = Array<FirstType[number] | SecondType[number]>;

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
```
Expand Down Expand Up @@ -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<typeof foo, typeof bar>;
// FooBar = (string | number)[]
const baz = (argument: FooBar) => {
// Do something
};
baz(fooBar);
```
@category Object
*/
export type Spread<
FirstType extends object,
SecondType extends object,
> = Simplify<Spread_<FirstType, SecondType>>;
FirstType extends Spreadable,
SecondType extends Spreadable,
> = FirstType extends TupleOrArray
? SecondType extends TupleOrArray
? SpreadTupleOrArray<FirstType, SecondType>
: Simplify<SpreadObject<FirstType, SecondType>>
: Simplify<SpreadObject<FirstType, SecondType>>;
38 changes: 38 additions & 0 deletions test-d/spread.ts
Expand Up @@ -42,3 +42,41 @@ const bar: Bar = {
const foobar = {...foo, ...bar};

expectType<FooBar>(foobar);

const arrayFoo = [1, 2, 3];
const arrayBar = [4, 5, 6];

const arrayFooBar = [...arrayFoo, ...arrayBar]; //=> number[]
type ArrayFooBar = Spread<typeof arrayFoo, typeof arrayBar>;

expectType<ArrayFooBar>(arrayFooBar);
expectType<number[]>(arrayFooBar);

const stringArray = ['4', '5', '6'];

const mixedArrayFooBar = [...arrayFoo, ...stringArray]; //=> (string | number)[]
type MixedArrayFooBar = Spread<typeof arrayFoo, typeof stringArray>;

expectType<MixedArrayFooBar>(mixedArrayFooBar);
expectType<Array<string | number>>(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<typeof tupleFoo, typeof tupleBar>;

expectType<TupleFooBar>(tupleFooBar);
expectType<Array<1 | 2 | 3 | 4 | 5 | 6>>(tupleFooBar);

const arrayTupleFooBar = [...arrayFoo, ...tupleBar]; //=> number[]
type ArrayTupleFooBar = Spread<typeof arrayFoo, typeof tupleBar>;

expectType<ArrayTupleFooBar>(arrayTupleFooBar);
expectType<number[]>(arrayTupleFooBar);

const tupleArrayFooBar = [...tupleFoo, ...arrayBar]; //=> number[]
type TupleArrayFooBar = Spread<typeof tupleFoo, typeof arrayBar>;

expectType<TupleArrayFooBar>(tupleArrayFooBar);
expectType<number[]>(tupleArrayFooBar);

0 comments on commit 96acab1

Please sign in to comment.