Skip to content

Commit

Permalink
Add RequireAllOrNone type (#254)
Browse files Browse the repository at this point in the history
  • Loading branch information
goodoldneon committed Sep 10, 2021
1 parent 3dfb58e commit 28199b0
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Expand Up @@ -11,6 +11,7 @@ export {Merge} from './source/merge';
export {MergeExclusive} from './source/merge-exclusive';
export {RequireAtLeastOne} from './source/require-at-least-one';
export {RequireExactlyOne} from './source/require-exactly-one';
export {RequireAllOrNone} from './source/require-all-or-none';
export {PartialDeep} from './source/partial-deep';
export {ReadonlyDeep} from './source/readonly-deep';
export {LiteralUnion} from './source/literal-union';
Expand Down
1 change: 1 addition & 0 deletions readme.md
Expand Up @@ -100,6 +100,7 @@ Click the type names for complete docs.
- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys.
- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys.
- [`RequireExactlyOne`](source/require-exactly-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more.
- [`RequireAllOrNone`](source/require-all-or-none.d.ts) - Create a type that requires all of the given keys or none of the given keys.
- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial<T>`](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<T>`](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).
Expand Down
36 changes: 36 additions & 0 deletions source/require-all-or-none.ts
@@ -0,0 +1,36 @@
/**
Create a type that requires all of the given keys or none of the given keys. The remaining keys are kept as is.
Use-cases:
- Creating interfaces for components with mutually-inclusive keys.
The caveat with `RequireAllOrNone` is that TypeScript doesn't always know at compile time every key that will exist at runtime. Therefore `RequireAllOrNone` can't do anything to prevent extra keys it doesn't know about.
@example
```
import {RequireAllOrNone} from 'type-fest';
type Responder = {
text?: () => string;
json?: () => string;
secure: boolean;
};
const responder1: RequireAllOrNone<Responder, 'text' | 'json'> = {
secure: true
};
const responder2: RequireAllOrNone<Responder, 'text' | 'json'> = {
text: () => '{"message": "hi"}',
json: () => '{"message": "ok"}',
secure: true
};
```
@category Utilities
*/
export type RequireAllOrNone<ObjectType, KeysType extends keyof ObjectType = never> = (
| Required<Pick<ObjectType, KeysType>> // Require all of the given keys.
| Partial<Record<KeysType, never>> // Require none of the given keys.
) &
Omit<ObjectType, KeysType>; // The rest of the keys.
24 changes: 24 additions & 0 deletions test-d/require-all-or-none.ts
@@ -0,0 +1,24 @@
import {expectError, expectAssignable} from 'tsd';
import {RequireAllOrNone} from '../index';

type SystemMessages = {
default: string;

macos: string;
linux: string;

optional?: string;
};

type ValidMessages = RequireAllOrNone<SystemMessages, 'macos' | 'linux'>;
const test = (_: ValidMessages): void => {}; // eslint-disable-line @typescript-eslint/no-empty-function

test({default: 'hello'});
test({macos: 'yo', linux: 'sup', optional: 'howdy', default: 'hello'});

expectError(test({}));
expectError(test({macos: 'hey', default: 'hello'}));
expectError(test({linux: 'hey', default: 'hello'}));

declare const oneWithoutKeys: RequireAllOrNone<{a: number; b: number}>;
expectAssignable<{a: number; b: number}>(oneWithoutKeys);

0 comments on commit 28199b0

Please sign in to comment.