Skip to content

Commit

Permalink
feat(json-crdt): 🎸 add "map" schema type
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Dec 5, 2023
1 parent 2714fa4 commit e8421d6
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/json-type/schema/SchemaBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ArraySchema,
ObjectSchema,
ObjectFieldSchema,
MapSchema,
NoT,
BinarySchema,
AnySchema,
Expand Down Expand Up @@ -49,6 +50,10 @@ export class SchemaBuilder {
return this.Object();
}

get map() {
return this.Map(this.any);
}

get bin() {
return this.Binary(this.any);
}
Expand Down Expand Up @@ -217,6 +222,10 @@ export class SchemaBuilder {
};
}

public Map<T extends Schema>(type: T, options?: Omit<NoT<MapSchema<T>>, 'type'>): MapSchema<T> {
return {__t: 'map', type, ...options};
}

public Any(options: NoT<AnySchema> = {}): AnySchema {
return {
__t: 'any',
Expand Down
10 changes: 10 additions & 0 deletions src/json-type/schema/__tests__/SchemaBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ describe('object', () => {
});
});

describe('map', () => {
test('can create an simple object using shorthand', () => {
expect(s.map).toEqual({__t: 'map', type: {__t: 'any'}});
});

test('can define a map', () => {
expect(s.Map(s.Boolean())).toEqual({__t: 'map', type: {__t: 'bool'}});
});
});

describe('or', () => {
test('can create an "or" type', () => {
const type = s.Or(s.str, s.num);
Expand Down
12 changes: 12 additions & 0 deletions src/json-type/schema/__tests__/TypeOf.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ test('can infer a simple object type', () => {
const obj4: T4 = {baz: 123, bazOptional: false};
});

test('can infer a map type', () => {
const schema1 = s.map;
const schema2 = s.Map(s.str);
const schema3 = s.Map(s.Array(s.num));
type T1 = TypeOf<typeof schema1>;
type T2 = TypeOf<typeof schema2>;
type T3 = TypeOf<typeof schema3>;
const obj1: Record<string, unknown> = {};
const obj2: T2 = {foo: 'bar'};
const obj3: T3 = {bar: [1, 2, 3]};
});

test('can infer a simple union type', () => {
const schema1 = s.Or(s.str, s.num);
const schema2 = s.Or(s.str, s.num, s.bool);
Expand Down
12 changes: 11 additions & 1 deletion src/json-type/schema/__tests__/type.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ObjectSchema, s} from '..';

test('can generate a type', () => {
test('can generate any type', () => {
const address: ObjectSchema = {
__t: 'obj',
title: 'User address',
Expand All @@ -14,6 +14,7 @@ test('can generate a type', () => {
s.prop('address', address),
s.prop('timeCreated', s.Number()),
s.prop('tags', s.Array(s.Or(s.Number(), s.String()))),
s.prop('elements', s.Map(s.str))
);

expect(userType).toMatchObject({
Expand Down Expand Up @@ -84,6 +85,15 @@ test('can generate a type', () => {
},
},
},
{
key: 'elements',
type: {
__t: 'map',
type: {
__t: 'str',
},
},
},
],
});
});
15 changes: 14 additions & 1 deletion src/json-type/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@ export interface ObjectOptionalFieldSchema<K extends string = string, V extends
optional: true;
}

/**
* Represents an object, which is treated as a map. All keys are strings and all
* values are of the same type.
*/
export interface MapSchema<T extends TType = any> extends TType<Record<string, unknown>>, WithValidator {
__t: 'map';
/** Type of all values in the map. */
type: T;
}

/**
* Reference to another type.
*/
Expand Down Expand Up @@ -276,7 +286,8 @@ export type JsonSchema =
| TupleSchema
| ObjectSchema
| ObjectFieldSchema
| ObjectOptionalFieldSchema;
| ObjectOptionalFieldSchema
| MapSchema;

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

Expand Down Expand Up @@ -304,6 +315,8 @@ export type TypeOfValue<T> = T extends BooleanSchema
? {[K in keyof U]: TypeOf<U[K]>}
: T extends ObjectSchema<infer F>
? NoEmptyInterface<TypeFields<Mutable<F>>>
: T extends MapSchema<infer U>
? Record<string, TypeOf<U>>
: T extends BinarySchema
? Uint8Array
: T extends FunctionSchema<infer Req, infer Res>
Expand Down

0 comments on commit e8421d6

Please sign in to comment.