From fde0aa740f9d4755f05dea0d9ea3ae61c7abb93b Mon Sep 17 00:00:00 2001 From: Kai Niedziela Date: Sun, 28 Jul 2019 14:24:37 +0200 Subject: [PATCH 1/8] create RequireOne type --- readme.md | 1 + source/require-one.d.ts | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 source/require-one.d.ts diff --git a/readme.md b/readme.md index 3f11c3c1e..7872a6c6f 100644 --- a/readme.md +++ b/readme.md @@ -69,6 +69,7 @@ Click the type names for complete docs. - [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. - [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties. - [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties. +- [`RequireOne`](source/require-one.d.ts) - Create a type that requires a single property of the given properties. - [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type. - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). diff --git a/source/require-one.d.ts b/source/require-one.d.ts new file mode 100644 index 000000000..13bd84bfe --- /dev/null +++ b/source/require-one.d.ts @@ -0,0 +1,23 @@ +/** +Create a type that requires only one of the given properties, but not more. The remaining properties are kept as is. + +@example +``` +import {RequireOne} from 'type-fest'; + +type Responder = { + text: () => string; + json: () => string; + secure: boolean; +}; +const responder: RequireOne = { + json: () => '{"message": "ok"}', + secure: true +}; +``` +*/ +export type RequireOne = + {[Key in KeysType]: ( + Required> & + Partial, never>> + )}[KeysType] & Omit; From a4f748be73c8fbf38c43609e81eccf975ea561d3 Mon Sep 17 00:00:00 2001 From: Kai Niedziela Date: Sun, 28 Jul 2019 20:06:47 +0200 Subject: [PATCH 2/8] add test cases for RequireOne type --- index.d.ts | 1 + readme.md | 2 +- test-d/require-one.ts | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 test-d/require-one.ts diff --git a/index.d.ts b/index.d.ts index b502ca7a4..b07cb7fc6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -7,6 +7,7 @@ export {Mutable} from './source/mutable'; export {Merge} from './source/merge'; export {MergeExclusive} from './source/merge-exclusive'; export {RequireAtLeastOne} from './source/require-at-least-one'; +export {RequireOne} from './source/require-one'; export {ReadonlyDeep} from './source/readonly-deep'; export {LiteralUnion} from './source/literal-union'; diff --git a/readme.md b/readme.md index 7872a6c6f..33e3f10b3 100644 --- a/readme.md +++ b/readme.md @@ -69,7 +69,7 @@ Click the type names for complete docs. - [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. - [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties. - [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties. -- [`RequireOne`](source/require-one.d.ts) - Create a type that requires a single property of the given properties. +- [`RequireOne`](source/require-one.d.ts) - Create a type that requires a single property of the given properties, but not more. - [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type. - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). diff --git a/test-d/require-one.ts b/test-d/require-one.ts new file mode 100644 index 000000000..73105e541 --- /dev/null +++ b/test-d/require-one.ts @@ -0,0 +1,24 @@ +import {expectType, expectError} from 'tsd'; +import {RequireOne} from '..'; + +type SystemMessages = { + default: string; + + macos: string; + linux: string; + + optional?: string; +}; + +type ValidMessages = RequireOne; +const test = (_: ValidMessages): void => {}; + +test({macos: 'hey', default: 'hello'}); +test({linux: 'sup', optional: 'howdy', default: 'hello'}); + +expectError(test({})); +expectError(test({macos: 'hey', linux: 'sup', default: 'hello'})); + +declare const oneWithoutKeys: RequireOne<{a: number; b: number}>; +expectType<{a: number} | {b: number}>(oneWithoutKeys); +expectError(expectType<{a: number; b: number}>(oneWithoutKeys)); From 02d6e80c1333af85170d8eaabec1d19895a81f38 Mon Sep 17 00:00:00 2001 From: Kai Niedziela Date: Sun, 28 Jul 2019 20:30:47 +0200 Subject: [PATCH 3/8] provide use cases for RequireOne type --- source/require-one.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/require-one.d.ts b/source/require-one.d.ts index 13bd84bfe..be848245f 100644 --- a/source/require-one.d.ts +++ b/source/require-one.d.ts @@ -1,6 +1,12 @@ /** Create a type that requires only one of the given properties, but not more. The remaining properties are kept as is. +Use cases: +- Creating interfaces for components that only need one of the properties to display properly. +- Declaring generic properties in a single place for a single use case that get narrowed down via `RequireOne`. + +The caveat with `RequireOne` is that TypeScript doesn't always know at compile time every property that will exist at runtime. Therefore `RequireOne` can't do anything to prevent extra properties it doesn't know about. + @example ``` import {RequireOne} from 'type-fest'; @@ -10,6 +16,7 @@ type Responder = { json: () => string; secure: boolean; }; + const responder: RequireOne = { json: () => '{"message": "ok"}', secure: true From 091a16b2ce9f6e2b6e924d88c23675270d0ae63d Mon Sep 17 00:00:00 2001 From: Kai Niedziela Date: Tue, 30 Jul 2019 15:18:32 +0200 Subject: [PATCH 4/8] Update require-one.ts Change spaces to tabs --- test-d/require-one.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-d/require-one.ts b/test-d/require-one.ts index 73105e541..c140c2a11 100644 --- a/test-d/require-one.ts +++ b/test-d/require-one.ts @@ -2,7 +2,7 @@ import {expectType, expectError} from 'tsd'; import {RequireOne} from '..'; type SystemMessages = { - default: string; + default: string; macos: string; linux: string; From 9bbaf42835b7a05c5626e8414d622f9913ab706b Mon Sep 17 00:00:00 2001 From: Kai Niedziela Date: Wed, 31 Jul 2019 04:52:56 +0200 Subject: [PATCH 5/8] Update source/require-one.d.ts Co-Authored-By: Dimitri Benin --- source/require-one.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/require-one.d.ts b/source/require-one.d.ts index be848245f..ada2e6df0 100644 --- a/source/require-one.d.ts +++ b/source/require-one.d.ts @@ -1,5 +1,5 @@ /** -Create a type that requires only one of the given properties, but not more. The remaining properties are kept as is. +Create a type that requires exactly one of the given properties, but not more. The remaining properties are kept as is. Use cases: - Creating interfaces for components that only need one of the properties to display properly. From e7cf2cfb2abad0d106e7fc3f34ad4d8802e48a08 Mon Sep 17 00:00:00 2001 From: Kai Niedziela Date: Wed, 7 Aug 2019 15:54:36 +0200 Subject: [PATCH 6/8] change RequireOne name to RequireExactlyOne --- index.d.ts | 2 +- readme.md | 2 +- source/require-exactly-one.d.ts | 30 +++++++++++++++++++ source/require-one.d.ts | 30 ------------------- ...{require-one.ts => require-exactly-one.ts} | 6 ++-- 5 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 source/require-exactly-one.d.ts delete mode 100644 source/require-one.d.ts rename test-d/{require-one.ts => require-exactly-one.ts} (73%) diff --git a/index.d.ts b/index.d.ts index b07cb7fc6..6420ff0c3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -7,7 +7,7 @@ export {Mutable} from './source/mutable'; export {Merge} from './source/merge'; export {MergeExclusive} from './source/merge-exclusive'; export {RequireAtLeastOne} from './source/require-at-least-one'; -export {RequireOne} from './source/require-one'; +export {RequireExactlyOne} from './source/require-exactly-one'; export {ReadonlyDeep} from './source/readonly-deep'; export {LiteralUnion} from './source/literal-union'; diff --git a/readme.md b/readme.md index 33e3f10b3..3fab591b4 100644 --- a/readme.md +++ b/readme.md @@ -69,7 +69,7 @@ Click the type names for complete docs. - [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. - [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties. - [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties. -- [`RequireOne`](source/require-one.d.ts) - Create a type that requires a single property of the given properties, but not more. +- [`RequireExactlyOne`](source/require-one.d.ts) - Create a type that requires exactly a single property of the given properties and disallows more. - [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type. - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). diff --git a/source/require-exactly-one.d.ts b/source/require-exactly-one.d.ts new file mode 100644 index 000000000..0b9cc2013 --- /dev/null +++ b/source/require-exactly-one.d.ts @@ -0,0 +1,30 @@ +/** +Create a type that requires exactly one of the given properties and disallows more. The remaining properties are kept as is. + +Use cases: +- Creating interfaces for components that only need one of the properties to display properly. +- Declaring generic properties in a single place for a single use case that get narrowed down via `RequireExactlyOne`. + +The caveat with `RequireExactlyOne` is that TypeScript doesn't always know at compile time every property that will exist at runtime. Therefore `RequireExactlyOne` can't do anything to prevent extra properties it doesn't know about. + +@example +``` +import {RequireExactlyOne} from 'type-fest'; + +type Responder = { + text: () => string; + json: () => string; + secure: boolean; +}; + +const responder: RequireExactlyOne = { + json: () => '{"message": "ok"}', + secure: true +}; +``` +*/ +export type RequireExactlyOne = + {[Key in KeysType]: ( + Required> & + Partial, never>> + )}[KeysType] & Omit; diff --git a/source/require-one.d.ts b/source/require-one.d.ts deleted file mode 100644 index ada2e6df0..000000000 --- a/source/require-one.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** -Create a type that requires exactly one of the given properties, but not more. The remaining properties are kept as is. - -Use cases: -- Creating interfaces for components that only need one of the properties to display properly. -- Declaring generic properties in a single place for a single use case that get narrowed down via `RequireOne`. - -The caveat with `RequireOne` is that TypeScript doesn't always know at compile time every property that will exist at runtime. Therefore `RequireOne` can't do anything to prevent extra properties it doesn't know about. - -@example -``` -import {RequireOne} from 'type-fest'; - -type Responder = { - text: () => string; - json: () => string; - secure: boolean; -}; - -const responder: RequireOne = { - json: () => '{"message": "ok"}', - secure: true -}; -``` -*/ -export type RequireOne = - {[Key in KeysType]: ( - Required> & - Partial, never>> - )}[KeysType] & Omit; diff --git a/test-d/require-one.ts b/test-d/require-exactly-one.ts similarity index 73% rename from test-d/require-one.ts rename to test-d/require-exactly-one.ts index c140c2a11..3b4564c56 100644 --- a/test-d/require-one.ts +++ b/test-d/require-exactly-one.ts @@ -1,5 +1,5 @@ import {expectType, expectError} from 'tsd'; -import {RequireOne} from '..'; +import {RequireExactlyOne} from '..'; type SystemMessages = { default: string; @@ -10,7 +10,7 @@ type SystemMessages = { optional?: string; }; -type ValidMessages = RequireOne; +type ValidMessages = RequireExactlyOne; const test = (_: ValidMessages): void => {}; test({macos: 'hey', default: 'hello'}); @@ -19,6 +19,6 @@ test({linux: 'sup', optional: 'howdy', default: 'hello'}); expectError(test({})); expectError(test({macos: 'hey', linux: 'sup', default: 'hello'})); -declare const oneWithoutKeys: RequireOne<{a: number; b: number}>; +declare const oneWithoutKeys: RequireExactlyOne<{a: number; b: number}>; expectType<{a: number} | {b: number}>(oneWithoutKeys); expectError(expectType<{a: number; b: number}>(oneWithoutKeys)); From 6af4e7e648621b0d757f49a755e9a1ffbb414304 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Mon, 16 Sep 2019 13:19:36 +0700 Subject: [PATCH 7/8] Update require-exactly-one.d.ts --- source/require-exactly-one.d.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/require-exactly-one.d.ts b/source/require-exactly-one.d.ts index 0b9cc2013..2be16b71e 100644 --- a/source/require-exactly-one.d.ts +++ b/source/require-exactly-one.d.ts @@ -1,9 +1,9 @@ /** Create a type that requires exactly one of the given properties and disallows more. The remaining properties are kept as is. -Use cases: +Use-cases: - Creating interfaces for components that only need one of the properties to display properly. -- Declaring generic properties in a single place for a single use case that get narrowed down via `RequireExactlyOne`. +- Declaring generic properties in a single place for a single use-case that gets narrowed down via `RequireExactlyOne`. The caveat with `RequireExactlyOne` is that TypeScript doesn't always know at compile time every property that will exist at runtime. Therefore `RequireExactlyOne` can't do anything to prevent extra properties it doesn't know about. @@ -18,6 +18,8 @@ type Responder = { }; const responder: RequireExactlyOne = { + // Adding a `text` property here would cause a compile error. + json: () => '{"message": "ok"}', secure: true }; From 21d2e86140e5ebfe42635c1cede0e50a0973d3bb Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Mon, 16 Sep 2019 13:20:56 +0700 Subject: [PATCH 8/8] Update readme.md --- readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.md b/readme.md index 86cd42767..590ee56d3 100644 --- a/readme.md +++ b/readme.md @@ -70,7 +70,6 @@ Click the type names for complete docs. - [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties. - [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties. - [`RequireExactlyOne`](source/require-one.d.ts) - Create a type that requires exactly a single property of the given properties and disallows more. -- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type. - [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) if you only need one level deep. - [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) if you only need one level deep. - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729).