Skip to content

Commit

Permalink
Add SetReadonly type (#624)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinigami92 committed Jun 14, 2023
1 parent 9c148ac commit b2e22fb
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type {Promisable} from './source/promisable';
export type {Opaque, UnwrapOpaque} from './source/opaque';
export type {InvariantOf} from './source/invariant-of';
export type {SetOptional} from './source/set-optional';
export type {SetReadonly} from './source/set-readonly';
export type {SetRequired} from './source/set-required';
export type {SetNonNullable} from './source/set-non-nullable';
export type {ValueOf} from './source/value-of';
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Click the type names for complete docs.
- [`UnwrapOpaque`](source/opaque.d.ts) - Revert an [opaque type](https://codemix.com/opaque-types-in-javascript/) back to its original type.
- [`InvariantOf`](source/invariant-of.d.ts) - Create an [invariant type](https://basarat.gitbook.io/typescript/type-system/type-compatibility#footnote-invariance), which is a type that does not accept supertypes and subtypes.
- [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional.
- [`SetReadonly`](source/set-readonly.d.ts) - Create a type that makes the given keys readonly.
- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required.
- [`SetNonNullable`](source/set-non-nullable.d.ts) - Create a type that makes the given keys non-nullable.
- [`ValueOf`](source/value-of.d.ts) - Create a union of the given object's values, and optionally specify which keys to get the values from.
Expand Down
38 changes: 38 additions & 0 deletions source/set-readonly.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type {Except} from './except';
import type {Simplify} from './simplify';

/**
Create a type that makes the given keys readonly. The remaining keys are kept as is.
Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are readonly.
@example
```
import type {SetReadonly} from 'type-fest';
type Foo = {
a: number;
readonly b: string;
c: boolean;
}
type SomeReadonly = SetReadonly<Foo, 'b' | 'c'>;
// type SomeReadonly = {
// a: number;
// readonly b: string; // Was already readonly and still is.
// readonly c: boolean; // Is now readonly.
// }
```
@category Object
*/
export type SetReadonly<BaseType, Keys extends keyof BaseType> =
// `extends unknown` is always going to be the case and is used to convert any
// union into a [distributive conditional
// type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
BaseType extends unknown
? Simplify<
Except<BaseType, Keys> &
Readonly<Pick<BaseType, Keys>>
>
: never;
18 changes: 18 additions & 0 deletions test-d/set-readonly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {expectNotAssignable, expectType} from 'tsd';
import type {SetReadonly} from '../index';

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

// Update two non readonly to readonly.
declare const variation2: SetReadonly<{readonly a: number; readonly b: string; c: boolean}, 'a' | 'b'>;
expectType<{readonly a: number; readonly b: string; c: boolean}>(variation2);

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

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

0 comments on commit b2e22fb

Please sign in to comment.