Skip to content

Commit

Permalink
Add SetRequired and SetOptional types (#48)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
crhistianramirez and sindresorhus committed Sep 14, 2019
1 parent 1c76afe commit b29c31a
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 0 deletions.
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export {ReadonlyDeep} from './source/readonly-deep';
export {LiteralUnion} from './source/literal-union';
export {Promisable} from './source/promisable';
export {Opaque} from './source/opaque';
export {SetOptional} from './source/set-optional';
export {SetRequired} from './source/set-required';

// Miscellaneous
export {PackageJson} from './source/package-json';
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ Click the type names for complete docs.
- [`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).
- [`Promisable`](source/promisable.d.ts) - Create a type that represents either the value or the value wrapped in `PromiseLike`.
- [`Opaque`](source/opaque.d.ts) - Create an [opaque type](https://codemix.com/opaque-types-in-javascript/).
- [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional.
- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required.

### Miscellaneous

Expand Down
32 changes: 32 additions & 0 deletions source/set-optional.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
Create a type that makes the given keys optional. The remaining keys are kept as is. The sister of the `SetRequired` type.
Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are optional.
@example
```
import {SetOptional} from 'type-fest';
type Foo = {
a: number;
b?: string;
c: boolean;
}
type SomeOptional = SetOptional<Foo, 'b' | 'c'>;
// type SomeOptional = {
// a: number;
// b?: string; // Was already optional and still is.
// c?: boolean; // Is now optional.
// }
```
*/
export type SetOptional<BaseType, Keys extends keyof BaseType = keyof BaseType> =
// Pick just the keys that are not optional from the base type.
Pick<BaseType, Exclude<keyof BaseType, Keys>> &
// Pick the keys that should be optional from the base type and make them optional.
Partial<Pick<BaseType, Keys>> extends
// If `InferredType` extends the previous, then for each key, use the inferred type key.
infer InferredType
? {[Property in keyof InferredType]: InferredType[Property]}
: never;
32 changes: 32 additions & 0 deletions source/set-required.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
Create a type that makes the given keys required. The remaining keys are kept as is. The sister of the `SetOptional` type.
Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are required.
@example
```
import {SetRequired} from 'type-fest';
type Foo = {
a?: number;
b: string;
c?: boolean;
}
type SomeRequired = SetRequired<Foo, 'b' | 'c'>;
// type SomeRequired = {
// a?: number;
// b: string; // Was already required and still is.
// c: boolean; // Is now required.
// }
```
*/
export type SetRequired<BaseType, Keys extends keyof BaseType = keyof BaseType> =
// Pick just the keys that are not required from the base type.
Pick<BaseType, Exclude<keyof BaseType, Keys>> &
// Pick the keys that should be required from the base type and make them required.
Required<Pick<BaseType, Keys>> extends
// If `InferredType` extends the previous, then for each key, use the inferred type key.
infer InferredType
? {[Property in keyof InferredType]: InferredType[Property]}
: never;
18 changes: 18 additions & 0 deletions test-d/set-optional.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {expectType, expectError} from 'tsd';
import {SetOptional} from '..';

// Update one required and one optional to optional.
declare const variation1: SetOptional<{a: number; b?: string; c: boolean}, 'b' | 'c'>;
expectType<{a: number; b?: string; c?: boolean}>(variation1);

// Update two required to optional.
declare const variation2: SetOptional<{a: number; b: string; c: boolean}, 'a' | 'b'>;
expectType<{a?: number; b?: string; c: boolean}>(variation2);

// Three optional remain optional.
declare const variation3: SetOptional<{a?: number; b?: string; c?: boolean}, 'a' | 'b' | 'c'>;
expectType<{a?: number; b?: string; c?: boolean}>(variation3);

// Fail if type changes even if optional is right.
declare const variation4: SetOptional<{a: number; b?: string; c: boolean}, 'b' | 'c'>;
expectError<{a: boolean; b?: string; c?: boolean}>(variation4);
18 changes: 18 additions & 0 deletions test-d/set-required.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {expectType, expectError} from 'tsd';
import {SetRequired} from '..';

// Update one required and one optional to required.
declare const variation1: SetRequired<{a?: number; b: string; c?: boolean}, 'b' | 'c'>;
expectType<{a?: number; b: string; c: boolean}>(variation1);

// Update two optional to required.
declare const variation2: SetRequired<{a?: number; b?: string; c?: boolean}, 'a' | 'b'>;
expectType<{a: number; b: string; c?: boolean}>(variation2);

// Three required remain required.
declare const variation3: SetRequired<{a: number; b: string; c: boolean}, 'a' | 'b' | 'c'>;
expectType<{a: number; b: string; c: boolean}>(variation3);

// Fail if type changes even if optional is right.
declare const variation4: SetRequired<{a?: number; b: string; c?: boolean}, 'b' | 'c'>;
expectError<{a?: boolean; b: string; c: boolean}>(variation4);

0 comments on commit b29c31a

Please sign in to comment.