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 Mutable type #157

Merged
merged 29 commits into from Dec 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a29eb5b
Add `SetMutable` type
kainiedziela Nov 26, 2020
552c1e4
Add `SetMutable` to readme
kainiedziela Nov 26, 2020
9b41e0f
Update set-mutable.d.ts
kainiedziela Nov 26, 2020
906a629
Update readme.md
sindresorhus Nov 28, 2020
f8588ad
Update base.d.ts
sindresorhus Nov 28, 2020
8fd0d33
Remove default type arg and add additional docs
kainiedziela Nov 28, 2020
9590e53
Update set-mutable.d.ts
sindresorhus Dec 1, 2020
b350c30
Update mutable.d.ts
sindresorhus Dec 1, 2020
dfeff63
Clarify relationships
kainiedziela Dec 1, 2020
103c63f
Update source/set-mutable.d.ts
kainiedziela Dec 1, 2020
b5fde12
Update wrong comments
kainiedziela Dec 1, 2020
b7b495b
Update source/mutable.d.ts
kainiedziela Dec 1, 2020
02b440b
Update source/mutable.d.ts
kainiedziela Dec 1, 2020
1af6d4a
Use `Simplify` type for flattening `Set-` types output
kainiedziela Dec 2, 2020
8a41605
Mege `SetMutable` with `Mutable`
kainiedziela Dec 2, 2020
50f5447
Update simplify.d.ts
sindresorhus Dec 16, 2020
d953a14
Update mutable.d.ts
sindresorhus Dec 16, 2020
2a43c51
Remove obsolete test file
kainiedziela Dec 17, 2020
4de5143
Improve documentation
kainiedziela Dec 17, 2020
5d0986f
Merge branch 'set-mutable' of https://github.com/kainiedziela/type-fe…
kainiedziela Dec 17, 2020
f6edcd1
Update source/simplify.d.ts
kainiedziela Dec 17, 2020
c93f096
Update source/mutable.d.ts
kainiedziela Dec 17, 2020
def1d3f
Improve documentation
kainiedziela Dec 17, 2020
fb52746
Merge branch 'set-mutable' of https://github.com/kainiedziela/type-fe…
kainiedziela Dec 17, 2020
c8cc662
Update readme.md
kainiedziela Dec 20, 2020
87a78c7
Update mutable.d.ts
sindresorhus Dec 26, 2020
a8f42ad
Merge branch 'master' into set-mutable
sindresorhus Dec 26, 2020
55c60e2
Import utility types into Mutable
kainiedziela Dec 26, 2020
0db9d7f
Merge branch 'set-mutable' of https://github.com/kainiedziela/type-fe…
kainiedziela Dec 26, 2020
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
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -61,7 +61,7 @@ Click the type names for complete docs.
### Utilities

- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type).
- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` keys into a mutable object. The inverse of `Readonly<T>`.
- [`Mutable`](source/mutable.d.ts) - Create a type that strips `readonly` from all or some of an object's keys. The inverse of `Readonly<T>`.
- [`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 keys.
- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys.
Expand Down
32 changes: 24 additions & 8 deletions source/mutable.d.ts
@@ -1,22 +1,38 @@
import {Except} from './except';
import {Simplify} from './simplify';

/**
Convert an object with `readonly` keys into a mutable object. Inverse of `Readonly<T>`.
Create a type that strips `readonly` from all or some of an object's keys. Inverse of `Readonly<T>`.

This can be used to [store and mutate options within a class](https://github.com/sindresorhus/pageres/blob/4a5d05fca19a5fbd2f53842cbf3eb7b1b63bddd2/source/index.ts#L72), [edit `readonly` objects within tests](https://stackoverflow.com/questions/50703834), and [construct a `readonly` object within a function](https://github.com/Microsoft/TypeScript/issues/24509).
This can be used to [store and mutate options within a class](https://github.com/sindresorhus/pageres/blob/4a5d05fca19a5fbd2f53842cbf3eb7b1b63bddd2/source/index.ts#L72), [edit `readonly` objects within tests](https://stackoverflow.com/questions/50703834), [construct a `readonly` object within a function](https://github.com/Microsoft/TypeScript/issues/24509), or to define a single model where the only thing that changes is whether or not some of the keys are mutable.

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

type Foo = {
readonly a: number;
readonly b: string;
readonly b: readonly string[]; // To show that only the mutability status of the properties, not their values, are affected.
readonly c: boolean;
};

const mutableFoo: Mutable<Foo> = {a: 1, b: '2'};
const mutableFoo: Mutable<Foo> = {a: 1, b: ['2']};
mutableFoo.a = 3;
mutableFoo.b[0] = 'new value'; // Will still fail as the value of property "b" is still a readonly type.
mutableFoo.b = ['something']; // Will work as the "b" property itself is no longer readonly.

type SomeMutable = Mutable<Foo, 'b' | 'c'>;
// type SomeMutable = {
// readonly a: number;
// b: readonly string[]; // It's now mutable. The type of the property remains unaffected.
// c: boolean; // It's now mutable.
// }
```
*/
export type Mutable<ObjectType> = {
// For each `Key` in the keys of `ObjectType`, make a mapped type by removing the `readonly` modifier from the key.
-readonly [KeyType in keyof ObjectType]: ObjectType[KeyType];
};
export type Mutable<BaseType, Keys extends keyof BaseType = keyof BaseType> =
Simplify<
// Pick just the keys that are not mutable from the base type.
Except<BaseType, Keys> &
// Pick the keys that should be mutable from the base type and make them mutable by removing the `readonly` modifier from the key.
{-readonly [KeyType in keyof Pick<BaseType, Keys>]: Pick<BaseType, Keys>[KeyType]}
>;
17 changes: 8 additions & 9 deletions source/set-optional.d.ts
@@ -1,4 +1,5 @@
import {Except} from './except';
import {Simplify} from './simplify';

/**
Create a type that makes the given keys optional. The remaining keys are kept as is. The sister of the `SetRequired` type.
Expand All @@ -23,12 +24,10 @@ type SomeOptional = SetOptional<Foo, 'b' | 'c'>;
// }
```
*/
export type SetOptional<BaseType, Keys extends keyof BaseType = keyof BaseType> =
// Pick just the keys that are not optional from the base type.
Except<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
? {[KeyType in keyof InferredType]: InferredType[KeyType]}
: never;
export type SetOptional<BaseType, Keys extends keyof BaseType> =
Simplify<
// Pick just the keys that are readonly from the base type.
Except<BaseType, Keys> &
// Pick the keys that should be mutable from the base type and make them mutable.
Partial<Pick<BaseType, Keys>>
>;
17 changes: 8 additions & 9 deletions source/set-required.d.ts
@@ -1,4 +1,5 @@
import {Except} from './except';
import {Simplify} from './simplify';

/**
Create a type that makes the given keys required. The remaining keys are kept as is. The sister of the `SetOptional` type.
Expand All @@ -23,12 +24,10 @@ type SomeRequired = SetRequired<Foo, 'b' | 'c'>;
// }
```
*/
export type SetRequired<BaseType, Keys extends keyof BaseType = keyof BaseType> =
// Pick just the keys that are not required from the base type.
Except<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
? {[KeyType in keyof InferredType]: InferredType[KeyType]}
: never;
export type SetRequired<BaseType, Keys extends keyof BaseType> =
Simplify<
// Pick just the keys that are optional from the base type.
Except<BaseType, Keys> &
// Pick the keys that should be required from the base type and make them required.
Required<Pick<BaseType, Keys>>
>;
4 changes: 4 additions & 0 deletions source/simplify.d.ts
@@ -0,0 +1,4 @@
/**
Flatten the type output to improve type hints shown in editors.
*/
export type Simplify<T> = {[KeyType in keyof T]: T[KeyType]};
17 changes: 17 additions & 0 deletions test-d/mutable.ts
@@ -1,3 +1,4 @@
import {expectType, expectError} from 'tsd';
import {Mutable} from '..';

type Foo = {
Expand All @@ -9,3 +10,19 @@ const ab: Mutable<Foo> = {a: 1, b: '2'};
ab.a = 2;
const ab2: Mutable<Readonly<Foo>> = ab;
ab2.a = 2;

// Update one mutable and one readonly to mutable, leaving one property unaffected.
declare const variation1: Mutable<{readonly a: number; b: string; readonly c: boolean}, 'b' | 'c'>;
expectType<{readonly a: number; b: string; c: boolean}>(variation1);

// Update two readonly to mutable, leaving one property unaffected.
declare const variation2: Mutable<{readonly a: number; readonly b: string; readonly c: boolean}, 'a' | 'b'>;
expectType<{a: number; b: string; readonly c: boolean}>(variation2);

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

// Check if type changes raise an error even if readonly and mutable are applied correctly.
declare const variation4: Mutable<{readonly a: number; b: string; readonly c: boolean}, 'b' | 'c'>;
expectError<{readonly a: boolean; b: string; c: boolean}>(variation4);