Skip to content

Commit

Permalink
feat: βž• add MarkReadonly (#298)
Browse files Browse the repository at this point in the history
* feat: βž• add MarkReadonly

* test: πŸ§ͺ MarkReadonly

* docs: πŸ“„ README

* docs: πŸ“„ changeset
  • Loading branch information
Beraliv committed Jan 31, 2022
1 parent 13504d0 commit e76a08a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/seven-socks-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ts-essentials": minor
---

Add `MarkReadonly` which sets readonly for specific keys
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ If you use any [functions](https://github.com/krzkaczor/ts-essentials/blob/maste
- [MergeN](#MergeN)
- [MarkRequired](#MarkRequired)
- [MarkOptional](#MarkOptional)
- [MarkReadonly](#MarkReadonly)
- [ReadonlyKeys](#ReadonlyKeys)
- [WritableKeys](#WritableKeys)
- [OptionalKeys](#OptionalKeys)
Expand Down Expand Up @@ -711,6 +712,30 @@ type UserWithoutPassword = MarkOptional<User, "password">;
// }
```

### MarkReadonly

Useful when you want to make some properties readonly without creating a separate type.

```typescript
interface User {
id: number;
name: string;
email: string;
password: string;
}

type UserThatCannotChangeName = MarkReadonly<User, "name">;

// Result:

// {
// id: number;
// readonly name: string;
// email: string;
// password: string;
// }
```

### ReadonlyKeys

Gets keys of an object which are readonly.
Expand Down
3 changes: 3 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@ export type MarkRequired<T, RK extends keyof T> = Omit<T, RK> & Required<Pick<T,
/** Mark some properties as optional, leaving others unchanged */
export type MarkOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

/** Mark some properties as readonly, leaving others unchanged */
export type MarkReadonly<T, K extends keyof T> = Omit<T, K> & Readonly<Pick<T, K>>;

/** Convert union type to intersection #darkmagic */
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

Expand Down
34 changes: 34 additions & 0 deletions test/mark-readonly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { AssertTrue as Assert, IsExact } from "conditional-type-checks";
import { MarkReadonly, WritableKeys, ReadonlyKeys } from "../lib/types";

function testMarkReadonly() {
type Example = {
readonly readonly1: Date;
readonly readonly2: RegExp;
required1: number;
required2: string;
optional1?: null;
optional2?: boolean;
};

type cases = [
Assert<IsExact<MarkReadonly<Example, never>, Example>>,
Assert<IsExact<MarkReadonly<Example, ReadonlyKeys<Example>>, Example>>,
Assert<IsExact<MarkReadonly<Example, WritableKeys<Example>>, Readonly<Example>>>,
Assert<
IsExact<
MarkReadonly<Example, "required1">,
{
readonly readonly1: Date;
readonly readonly2: RegExp;
readonly required1: number;
required2: string;
optional1?: null;
optional2?: boolean;
}
>
>,
// @ts-expect-error do NOT support union types
MarkReadonly<Example | { a: 1 }, "required1">,
];
}

0 comments on commit e76a08a

Please sign in to comment.