From ceedbc98c1ab7bca37f8c45a702cf49570d97df9 Mon Sep 17 00:00:00 2001 From: Patrick Keenan Date: Sat, 27 May 2023 01:14:25 -0700 Subject: [PATCH] feat(object/types): init - `Key` is a valid object key. - `EmptyObject` is an object for which all properties are invalid. - `Entry` is a key-value tuple. - `Pair` is a key-value pair in an object. - `EntryToPair` converts an `Entry` to a `Pair` - `FromEntries` converts `Entry`s to an object - `ToEntries` converts an object to an array of `Entry`s - The `Obj` namespace clusters these helper types. --- object/mod.ts | 1 + object/types.ts | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 12 +++++++++ 3 files changed, 84 insertions(+) create mode 100644 object/types.ts diff --git a/object/mod.ts b/object/mod.ts index 0e556c4..b47e50e 100644 --- a/object/mod.ts +++ b/object/mod.ts @@ -1 +1,2 @@ +export * from "./types.ts"; export * from "./utils.ts"; diff --git a/object/types.ts b/object/types.ts new file mode 100644 index 0000000..1a0679b --- /dev/null +++ b/object/types.ts @@ -0,0 +1,71 @@ +import type { Pretty } from "../ts/types.ts"; + +/** A valid object key. */ +export type Key = string | number | symbol; + +/** An object with no keys. + * + * @example + * const empty: EmptyObject = {}; + * const empty2: EmptyObject = { a: 1 }; // Error */ +export type EmptyObject = Record; + +/** A key-value tuple. Similar to the outputs of `Object.entries` and the inputs + * to `Object.fromEntries`, except numbers are not stringified and symbols are + * allowed. This is primarily used for `extends` clauses to ensure that an input + * conforms to this shape. + * + * @example + * type MyEntry = Entry; // [Key, unknown] */ +export type Entry = [key: Key, value: V]; + +/** An object intended to house a single key-value pair. The object-shaped + * equivalent of `Entry`. Mostly the same as TS's builtin `Record` type but + * provided here for parity with other object types. + * + * @example + * type MyPair = Pair<"a", 1>; + * // { a: 1 } */ +export type Pair = Pretty<{ [_ in K]: V }>; + +/** Converts an `Entry` to an `Pair`. + * + * @example + * type MyPair = EntryToPair<["a", 1]>; + * // { a: 1 } */ +export type EntryToPair = T extends + [infer K extends Key, infer Value] ? Pair + : never; + +type _EntriesToObject = T extends [infer Head extends Entry, ...infer Tail] + ? EntryToPair & _EntriesToObject + : Record; + +/** Converts an array of `Entry`s to an object. Analogous to + * `Object.fromEntries`. + * + * @example + * type MyObj = FromEntries<[["a", 1], ["b", 2]]>; + * // { a: 1, b: 2 } */ +export type FromEntries = Pretty<_EntriesToObject>; + +/** Convert an object to an array of `Entry`s. Unfortunately, this cannot return + * a tuple which would preserve the order of the keys, due to current + * limitations in TypeScript. + * + * @example + * type MyEntries = Obj.ToEntries<{ a: 1, b: 2 }>; + * // Array<["a", 1], ["b", 2]> */ +export type ToEntries = { [K in keyof T]: [K, T[K]] }[keyof T][]; + +export declare namespace Obj { + export { + EmptyObject as Empty, + Entry, + EntryToPair, + FromEntries, + Key, + Pair, + ToEntries, + }; +} diff --git a/readme.md b/readme.md index e72edd9..92dab5a 100644 --- a/readme.md +++ b/readme.md @@ -242,6 +242,18 @@ const S = Symbol("symbol"); setNestedEntry({}, ["a", 10, S], "👋"); // { a: { 10: { [S]: "👋" } } } ``` +```ts +import type { Obj } from "https://deno.land/x/handy/object/types.ts"; + +type Key = Obj.Key; // string | number | symbol +type Empty = Obj.Entry; // Record +type Entry = Obj.Entry; // [Key, any] +type Pair = Obj.Pair<"a", number>; // { "a": number } +type EntryToPair = Obj.EntryToPair; // Pair +type MyObj = Obj.FromEntries<[["a", 1], ["b", null]]>; // { a: 1, b: null } +type Entries = Obj.ToEntries; // Array<["a", 1], ["b", null]> +``` + ## `os` OS-related utilities.