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 Spread type #427

Merged
merged 2 commits into from Jul 30, 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
1 change: 1 addition & 0 deletions index.d.ts
Expand Up @@ -61,6 +61,7 @@ export {OptionalKeysOf} from './source/optional-keys-of';
export {HasOptionalKeys} from './source/has-optional-keys';
export {RequiredKeysOf} from './source/required-keys-of';
export {HasRequiredKeys} from './source/has-required-keys';
export {Spread} from './source/spread';

// Template literal types
export {CamelCase} from './source/camel-case';
Expand Down
1 change: 1 addition & 0 deletions readme.md
Expand Up @@ -161,6 +161,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.

### JSON

Expand Down
54 changes: 54 additions & 0 deletions source/spread.d.ts
@@ -0,0 +1,54 @@
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> = {
[Key in keyof FirstType]: Key extends keyof SecondType
? FirstType[Key] | Required<SecondType>[Key]
: FirstType[Key];
} & Pick<
SecondType,
RequiredKeysOf<SecondType> | Exclude<keyof SecondType, keyof FirstType>
>;

/**
Mimic the type inferred by TypeScript when merging two objects using the spread operator.

@example
```
import type {Spread} from 'type-fest';

type Foo = {
a: number;
b?: string;
};

type Bar = {
b?: number;
c: boolean;
};

const foo = {a: 1, b: '2'};
const bar = {c: false};
const fooBar = {...foo, ...bar};

type FooBar = Spread<Foo, Bar>;
// type FooBar = {
// a: number;
// b?: string | number | undefined;
// c: boolean;
// }

const baz = (argument: FooBar) => {
// Do something
}

baz(fooBar);
```

@category Object
*/
export type Spread<
FirstType extends object,
SecondType extends object,
> = Simplify<Spread_<FirstType, SecondType>>;
44 changes: 44 additions & 0 deletions test-d/spread.ts
@@ -0,0 +1,44 @@
import {expectType} from 'tsd';
import type {Spread} from '../index';

type Foo = {
a: 'a1';
b?: 'b1';
c: 'c1';
d?: 'd1';
e: 'e1' | undefined;
f: 'f1';
g?: 'g1';
};

type Bar = {
a?: 'a2';
b: 'b2';
c: 'c2';
d?: 'd2';
e?: 'e2';
h: 'h2';
i?: 'i2';
};

type FooBar = Spread<Foo, Bar>;

const foo: Foo = {
a: 'a1',
b: 'b1',
c: 'c1',
d: 'd1',
e: 'e1',
f: 'f1',
g: 'g1',
};

const bar: Bar = {
b: 'b2',
c: 'c2',
h: 'h2',
};

const foobar = {...foo, ...bar};

expectType<FooBar>(foobar);