Skip to content

Commit e8421d6

Browse files
committed
feat(json-crdt): 🎸 add "map" schema type
1 parent 2714fa4 commit e8421d6

File tree

5 files changed

+56
-2
lines changed

5 files changed

+56
-2
lines changed

src/json-type/schema/SchemaBuilder.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
ArraySchema,
66
ObjectSchema,
77
ObjectFieldSchema,
8+
MapSchema,
89
NoT,
910
BinarySchema,
1011
AnySchema,
@@ -49,6 +50,10 @@ export class SchemaBuilder {
4950
return this.Object();
5051
}
5152

53+
get map() {
54+
return this.Map(this.any);
55+
}
56+
5257
get bin() {
5358
return this.Binary(this.any);
5459
}
@@ -217,6 +222,10 @@ export class SchemaBuilder {
217222
};
218223
}
219224

225+
public Map<T extends Schema>(type: T, options?: Omit<NoT<MapSchema<T>>, 'type'>): MapSchema<T> {
226+
return {__t: 'map', type, ...options};
227+
}
228+
220229
public Any(options: NoT<AnySchema> = {}): AnySchema {
221230
return {
222231
__t: 'any',

src/json-type/schema/__tests__/SchemaBuilder.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ describe('object', () => {
5959
});
6060
});
6161

62+
describe('map', () => {
63+
test('can create an simple object using shorthand', () => {
64+
expect(s.map).toEqual({__t: 'map', type: {__t: 'any'}});
65+
});
66+
67+
test('can define a map', () => {
68+
expect(s.Map(s.Boolean())).toEqual({__t: 'map', type: {__t: 'bool'}});
69+
});
70+
});
71+
6272
describe('or', () => {
6373
test('can create an "or" type', () => {
6474
const type = s.Or(s.str, s.num);

src/json-type/schema/__tests__/TypeOf.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ test('can infer a simple object type', () => {
119119
const obj4: T4 = {baz: 123, bazOptional: false};
120120
});
121121

122+
test('can infer a map type', () => {
123+
const schema1 = s.map;
124+
const schema2 = s.Map(s.str);
125+
const schema3 = s.Map(s.Array(s.num));
126+
type T1 = TypeOf<typeof schema1>;
127+
type T2 = TypeOf<typeof schema2>;
128+
type T3 = TypeOf<typeof schema3>;
129+
const obj1: Record<string, unknown> = {};
130+
const obj2: T2 = {foo: 'bar'};
131+
const obj3: T3 = {bar: [1, 2, 3]};
132+
});
133+
122134
test('can infer a simple union type', () => {
123135
const schema1 = s.Or(s.str, s.num);
124136
const schema2 = s.Or(s.str, s.num, s.bool);

src/json-type/schema/__tests__/type.spec.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {ObjectSchema, s} from '..';
22

3-
test('can generate a type', () => {
3+
test('can generate any type', () => {
44
const address: ObjectSchema = {
55
__t: 'obj',
66
title: 'User address',
@@ -14,6 +14,7 @@ test('can generate a type', () => {
1414
s.prop('address', address),
1515
s.prop('timeCreated', s.Number()),
1616
s.prop('tags', s.Array(s.Or(s.Number(), s.String()))),
17+
s.prop('elements', s.Map(s.str))
1718
);
1819

1920
expect(userType).toMatchObject({
@@ -84,6 +85,15 @@ test('can generate a type', () => {
8485
},
8586
},
8687
},
88+
{
89+
key: 'elements',
90+
type: {
91+
__t: 'map',
92+
type: {
93+
__t: 'str',
94+
},
95+
},
96+
},
8797
],
8898
});
8999
});

src/json-type/schema/schema.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ export interface ObjectOptionalFieldSchema<K extends string = string, V extends
219219
optional: true;
220220
}
221221

222+
/**
223+
* Represents an object, which is treated as a map. All keys are strings and all
224+
* values are of the same type.
225+
*/
226+
export interface MapSchema<T extends TType = any> extends TType<Record<string, unknown>>, WithValidator {
227+
__t: 'map';
228+
/** Type of all values in the map. */
229+
type: T;
230+
}
231+
222232
/**
223233
* Reference to another type.
224234
*/
@@ -276,7 +286,8 @@ export type JsonSchema =
276286
| TupleSchema
277287
| ObjectSchema
278288
| ObjectFieldSchema
279-
| ObjectOptionalFieldSchema;
289+
| ObjectOptionalFieldSchema
290+
| MapSchema;
280291

281292
export type Schema = JsonSchema | RefSchema | OrSchema | AnySchema | FunctionSchema | FunctionStreamingSchema;
282293

@@ -304,6 +315,8 @@ export type TypeOfValue<T> = T extends BooleanSchema
304315
? {[K in keyof U]: TypeOf<U[K]>}
305316
: T extends ObjectSchema<infer F>
306317
? NoEmptyInterface<TypeFields<Mutable<F>>>
318+
: T extends MapSchema<infer U>
319+
? Record<string, TypeOf<U>>
307320
: T extends BinarySchema
308321
? Uint8Array
309322
: T extends FunctionSchema<infer Req, infer Res>

0 commit comments

Comments
 (0)