Skip to content

Commit

Permalink
feat(object/types): init
Browse files Browse the repository at this point in the history
- `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.
  • Loading branch information
pskfyi authored and AustinArey committed May 27, 2023
1 parent 18a8ee7 commit 5da75ff
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
1 change: 1 addition & 0 deletions object/mod.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./types.ts";
export * from "./utils.ts";
71 changes: 71 additions & 0 deletions object/types.ts
Original file line number Diff line number Diff line change
@@ -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<Key, never>;

/** 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<unknown>; // [Key, unknown] */
export type Entry<V = unknown> = [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<K extends Key, V> = Pretty<{ [_ in K]: V }>;

/** Converts an `Entry` to an `Pair`.
*
* @example
* type MyPair = EntryToPair<["a", 1]>;
* // { a: 1 } */
export type EntryToPair<T extends Entry> = T extends
[infer K extends Key, infer Value] ? Pair<K, Value>
: never;

type _EntriesToObject<T> = T extends [infer Head extends Entry, ...infer Tail]
? EntryToPair<Head> & _EntriesToObject<Tail>
: Record<never, never>;

/** 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<T extends Entry[]> = Pretty<_EntriesToObject<T>>;

/** 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<T> = { [K in keyof T]: [K, T[K]] }[keyof T][];

export declare namespace Obj {
export {
EmptyObject as Empty,
Entry,
EntryToPair,
FromEntries,
Key,
Pair,
ToEntries,
};
}
12 changes: 12 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,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<Key, never>
type Entry = Obj.Entry<any>; // [Key, any]
type Pair = Obj.Pair<"a", number>; // { "a": number }
type EntryToPair = Obj.EntryToPair<Entry>; // Pair
type MyObj = Obj.FromEntries<[["a", 1], ["b", null]]>; // { a: 1, b: null }
type Entries = Obj.ToEntries<MyObj>; // Array<["a", 1], ["b", null]>
```

## `os`

OS-related utilities.
Expand Down

0 comments on commit 5da75ff

Please sign in to comment.