From a3e1589d540f7fa4ac70f1d279ea30c48b04b7f1 Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Sat, 20 May 2023 11:12:51 +0200 Subject: [PATCH] Add `WritableKeysOf` and `ReadonlyKeysOf` type (#619) --- index.d.ts | 2 ++ readme.md | 2 ++ source/readonly-keys-of.d.ts | 29 +++++++++++++++++++++++++++++ source/writable-keys-of.d.ts | 30 ++++++++++++++++++++++++++++++ test-d/readonly-keys-of.ts | 29 +++++++++++++++++++++++++++++ test-d/writable-keys-of.ts | 29 +++++++++++++++++++++++++++++ 6 files changed, 121 insertions(+) create mode 100644 source/readonly-keys-of.d.ts create mode 100644 source/writable-keys-of.d.ts create mode 100644 test-d/readonly-keys-of.ts create mode 100644 test-d/writable-keys-of.ts diff --git a/index.d.ts b/index.d.ts index 359c1c5c5..1f0c540f7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -77,6 +77,8 @@ export type {OverrideProperties} from './source/override-properties'; export type {HasOptionalKeys} from './source/has-optional-keys'; export type {RequiredKeysOf} from './source/required-keys-of'; export type {HasRequiredKeys} from './source/has-required-keys'; +export type {ReadonlyKeysOf} from './source/readonly-keys-of'; +export type {WritableKeysOf} from './source/writable-keys-of'; export type {Spread} from './source/spread'; export type {TupleToUnion} from './source/tuple-to-union'; export type {IsEqual} from './source/is-equal'; diff --git a/readme.md b/readme.md index e09c61f6a..db4c58ee2 100644 --- a/readme.md +++ b/readme.md @@ -143,6 +143,8 @@ Click the type names for complete docs. - [`HasOptionalKeys`](source/has-optional-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any optional fields. - [`RequiredKeysOf`](source/required-keys-of.d.ts) - Extract all required keys from the given type. - [`HasRequiredKeys`](source/has-required-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any required fields. +- [`ReadonlyKeysOf`](source/readonly-keys-of.d.ts) - Extract all writable (non-readonly) keys from the given type. +- [`WritableKeysOf`](source/writable-keys-of.d.ts) - Extract all readonly keys from the given type. - [`Spread`](source/spread.d.ts) - Mimic the type inferred by TypeScript when merging two objects or two arrays/tuples using the spread syntax. - [`IsEqual`](source/is-equal.d.ts) - Returns a boolean for whether the two given types are equal. - [`TaggedUnion`](source/tagged-union.d.ts) - Create a union of types that share a common discriminant property. diff --git a/source/readonly-keys-of.d.ts b/source/readonly-keys-of.d.ts new file mode 100644 index 000000000..f7c31f406 --- /dev/null +++ b/source/readonly-keys-of.d.ts @@ -0,0 +1,29 @@ +import type {IsEqual} from './is-equal'; + +/** +Extract all readonly keys from the given type. + +This is useful when you want to create a new type that contains readonly keys only. + +@example +``` +import type {ReadonlyKeysOf} from 'type-fest'; + +interface User { + name: string; + surname: string; + readonly id: number; +} + +type UpdateResponse = Pick>; + +const update1: UpdateResponse = { + id: 123, +}; +``` + +@category Utilities +*/ +export type ReadonlyKeysOf = NonNullable<{ + [P in keyof T]: IsEqual<{[Q in P]: T[P]}, {readonly [Q in P]: T[P]}> extends true ? P : never +}[keyof T]>; diff --git a/source/writable-keys-of.d.ts b/source/writable-keys-of.d.ts new file mode 100644 index 000000000..7432c9f29 --- /dev/null +++ b/source/writable-keys-of.d.ts @@ -0,0 +1,30 @@ +import type {IsEqual} from './is-equal'; + +/** +Extract all writable keys from the given type. + +This is useful when you want to create a new type that contains writable keys only. + +@example +``` +import type {WritableKeysOf} from 'type-fest'; + +interface User { + name: string; + surname: string; + readonly id: number; +} + +type UpdateRequest = Pick>; + +const update1: UpdateRequest = { + name: 'Alice', + surname: 'Acme', +}; +``` + +@category Utilities +*/ +export type WritableKeysOf = NonNullable<{ + [P in keyof T]: IsEqual<{[Q in P]: T[P]}, {readonly [Q in P]: T[P]}> extends false ? P : never +}[keyof T]>; diff --git a/test-d/readonly-keys-of.ts b/test-d/readonly-keys-of.ts new file mode 100644 index 000000000..637f71b1d --- /dev/null +++ b/test-d/readonly-keys-of.ts @@ -0,0 +1,29 @@ +import {expectType} from 'tsd'; +import type {ReadonlyKeysOf} from '../index'; + +type TestType1 = { + a: string; + readonly b: boolean; +}; + +type TestType2 = { + readonly a: string; + readonly b: boolean; +}; + +type TestType3 = { + a: string; + b: boolean; +}; + +type ReadonlyKeysOf1 = ReadonlyKeysOf; +type ReadonlyKeysOf2 = ReadonlyKeysOf; +type ReadonlyKeysOf3 = ReadonlyKeysOf; + +declare const test1: ReadonlyKeysOf1; +declare const test2: ReadonlyKeysOf2; +declare const test3: ReadonlyKeysOf3; + +expectType<'b'>(test1); +expectType<'a' | 'b'>(test2); +expectType(test3); diff --git a/test-d/writable-keys-of.ts b/test-d/writable-keys-of.ts new file mode 100644 index 000000000..caad4754e --- /dev/null +++ b/test-d/writable-keys-of.ts @@ -0,0 +1,29 @@ +import {expectType} from 'tsd'; +import type {WritableKeysOf} from '../index'; + +type TestType1 = { + readonly a: string; + b: boolean; +}; + +type TestType2 = { + a: string; + b: boolean; +}; + +type TestType3 = { + readonly a: string; + readonly b: boolean; +}; + +type WritableKeysOf1 = WritableKeysOf; +type WritableKeysOf2 = WritableKeysOf; +type WritableKeysOf3 = WritableKeysOf; + +declare const test1: WritableKeysOf1; +declare const test2: WritableKeysOf2; +declare const test3: WritableKeysOf3; + +expectType<'b'>(test1); +expectType<'a' | 'b'>(test2); +expectType(test3);