Skip to content

Commit 62d3fdf

Browse files
authored
feat: introduce record types
1 parent 19f26e1 commit 62d3fdf

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ if (is(data, dog)) {
227227
}
228228
```
229229

230+
### Record Types
231+
232+
It's also possible to use `record` passing the type for the key and value:
233+
234+
```ts
235+
assert(value, record(String, String));
236+
// value.foo is a string
237+
```
238+
230239
### Union Types
231240

232241
The package exports a `union` function to check if a value is of either of the given types:

src/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,23 @@ export function assert<T extends Schema>(
179179
throw new TypeError(`${mismatch[0]} does not match the schema.`);
180180
}
181181
}
182+
183+
/**
184+
* Valid Record keys
185+
*/
186+
export type RecordKeys =
187+
| StringConstructor
188+
| NumberConstructor
189+
| string
190+
| number
191+
192+
/**
193+
* Creates a type guard that checks if a value matches a given Record<K, V>.
194+
*/
195+
export function record<const K extends RecordKeys, const V extends Schema>(key: K, value: V) {
196+
return (v: unknown): v is Record<TypeFromSchema<K>, TypeFromSchema<V>> =>
197+
typeof v === 'object'
198+
&& v !== null
199+
&& Object.values(v).every((y) => is(y, value))
200+
&& Object.keys(v).every((y) => is(y, key))
201+
}

src/test/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
diff,
66
is,
77
optional,
8+
record,
89
typeGuard,
910
union,
1011
unknown,
@@ -301,3 +302,11 @@ tap.test("deeply nested schemas", async (t) => {
301302
const value: S = {};
302303
t.ok(is(value, schema));
303304
});
305+
306+
tap.test("record", async (t) => {
307+
const obj: unknown = { foo: "foo" };
308+
assert(obj, record(String, String));
309+
t.throws(() => {
310+
assert(obj, record(String, Number));
311+
}, TypeError);
312+
});

0 commit comments

Comments
 (0)