Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add array/tuple support to Spread #435

Merged
merged 5 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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);