From efba9f4709ef1f76cf30fcd4a3ca834822a132bd Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Sat, 30 Jul 2022 14:22:37 +0200 Subject: [PATCH] Add `Spread` type (#427) --- index.d.ts | 1 + readme.md | 1 + source/spread.d.ts | 54 ++++++++++++++++++++++++++++++++++++++++++++++ test-d/spread.ts | 44 +++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 source/spread.d.ts create mode 100644 test-d/spread.ts diff --git a/index.d.ts b/index.d.ts index 95eba8c22..a8bdc753b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -62,6 +62,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'; diff --git a/readme.md b/readme.md index 5e8d46c6a..14532ca12 100644 --- a/readme.md +++ b/readme.md @@ -162,6 +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. ### JSON diff --git a/source/spread.d.ts b/source/spread.d.ts new file mode 100644 index 000000000..395263653 --- /dev/null +++ b/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_ = { + [Key in keyof FirstType]: Key extends keyof SecondType + ? FirstType[Key] | Required[Key] + : FirstType[Key]; +} & Pick< + SecondType, + RequiredKeysOf | Exclude +>; + +/** +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; +// 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>; diff --git a/test-d/spread.ts b/test-d/spread.ts new file mode 100644 index 000000000..48ae8ba1a --- /dev/null +++ b/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; + +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);