Skip to content

Commit f863c97

Browse files
committed
Type refinements
1 parent 0d51599 commit f863c97

File tree

4 files changed

+87
-35
lines changed

4 files changed

+87
-35
lines changed

src/Model/Doc.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { type Segments } from './types';
33
import { Collection } from './collections';
44

55
export class Doc {
6-
collectionName: string;
7-
id: string;
86
collectionData: Model;
7+
collectionName: string;
98
data: any;
9+
id: string;
1010
model: Model;
1111

1212
constructor(model: Model, collectionName: string, id: string, data: any, _collection?: Collection) {

src/Model/Query.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { type Segments } from './types';
33
import { ChildModel, ErrorCallback, Model } from './Model';
44
import { CollectionMap } from './CollectionMap';
55
import { ModelData } from '.';
6-
import { Doc } from 'sharedb';
6+
import type { Doc } from './Doc';
7+
import type { Doc as ShareDBDoc } from 'sharedb';
8+
import type { RemoteDoc } from './RemoteDoc';
79

810
var defaultType = require('sharedb/lib/client').types.defaultType;
911
var util = require('../util');
@@ -267,7 +269,7 @@ export class Query<T = {}> {
267269

268270
fetchPromised = promisify(Query.prototype.fetch);
269271

270-
subscribe(cb) {
272+
subscribe(cb: ErrorCallback) {
271273
cb = this.model.wrapCallback(cb);
272274
this.context.subscribeQuery(this);
273275

@@ -303,9 +305,9 @@ export class Query<T = {}> {
303305

304306
subscribePromised = promisify(Query.prototype.subscribe);
305307

306-
_subscribeCb(cb) {
308+
_subscribeCb(cb: ErrorCallback) {
307309
var query = this;
308-
return function subscribeCb(err: Error, results: Doc[], extra?: any) {
310+
return function subscribeCb(err: Error, results: ShareDBDoc[], extra?: any) {
309311
if (err) return query._flushSubscribeCallbacks(err, cb);
310312
query._setExtra(extra);
311313
query._setResults(results);
@@ -405,7 +407,7 @@ export class Query<T = {}> {
405407
if (extra === undefined) return;
406408
this.model._setDiffDeep(this.extraSegments, extra);
407409
};
408-
_setResults(results) {
410+
_setResults(results: ShareDBDoc[]) {
409411
var ids = resultsIds(results);
410412
this._setResultIds(ids);
411413
};
@@ -507,7 +509,7 @@ export class Query<T = {}> {
507509
var results = [];
508510
for (var i = 0; i < ids.length; i++) {
509511
var id = ids[i];
510-
var doc = collection.docs[id];
512+
var doc = collection.docs[id] as RemoteDoc;
511513
results.push(doc && doc.shareDoc);
512514
}
513515
return results;
@@ -526,7 +528,7 @@ export class Query<T = {}> {
526528
var collection = this.model.getCollection(this.collectionName);
527529
for (var i = 0, l = ids.length; i < l; i++) {
528530
var id = ids[i];
529-
var doc = collection && collection.docs[id];
531+
var doc = (collection && collection.docs[id]) as RemoteDoc;
530532
results.push(doc && doc.get());
531533
}
532534
return results;
@@ -564,7 +566,7 @@ export class Query<T = {}> {
564566
results = [];
565567
for (var i = 0; i < ids.length; i++) {
566568
var id = ids[i];
567-
var doc = collection.docs[id];
569+
var doc = collection.docs[id] as RemoteDoc;
568570
if (doc) {
569571
delete collection.docs[id];
570572
var data = doc.shareDoc.data;
@@ -610,7 +612,7 @@ function queryHash(contextId, collectionName, expression, options) {
610612
return JSON.stringify(args).replace(/\./g, '|');
611613
}
612614

613-
function resultsIds(results) {
615+
function resultsIds(results: { id: string }[]): string[] {
614616
var ids = [];
615617
for (var i = 0; i < results.length; i++) {
616618
var shareDoc = results[i];

src/Model/collections.ts

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,78 @@
11
import { type Segments } from './types';
22
import { Doc } from './Doc';
33
import { Model, RootModel } from './Model';
4+
import { JSONObject } from 'sharedb/lib/sharedb';
5+
import { VerifyJsonWebKeyInput } from 'crypto';
6+
import { Path, ReadonlyDeep, ShallowCopiedValue } from '../types';
47
var LocalDoc = require('./LocalDoc');
58
var util = require('../util');
69

710
export class ModelCollections {
811
docs: Record<string, any>;
912
}
10-
export class ModelData {}
11-
export class DocMap {}
12-
export class CollectionData {}
13+
14+
/** Root model data */
15+
export class ModelData {
16+
[collectionName: string]: CollectionData<JSONObject>;
17+
}
18+
19+
class DocMap {
20+
[id: string]: Doc;
21+
}
22+
23+
/** Dictionary of document id to document data */
24+
export class CollectionData<T extends JSONObject> {
25+
[id: string]: T;
26+
}
1327

1428
declare module './Model' {
1529
interface RootModel {
1630
collections: ModelCollections;
1731
data: ModelData;
1832
}
19-
interface Model {
33+
interface Model<T> {
2034
destroy(subpath?: string): void;
21-
get(subpath?: string): any;
22-
get<T>(subpath?: string): T;
23-
getCollection(collecitonName: string): ModelCollections;
24-
getCopy(subpath: string): any;
25-
getDeepCopy(subpath: string): any;
35+
36+
/**
37+
* Gets the value located at this model's path or a relative subpath.
38+
*
39+
* If no value exists at the path, this returns `undefined`.
40+
*
41+
* _Note:_ The value is returned by reference, and object values should not
42+
* be directly modified - use the Model mutator methods instead. The
43+
* TypeScript compiler will enforce no direct modifications, but there are
44+
* no runtime guards, which means JavaScript source code could still
45+
* improperly make direct modifications.
46+
*
47+
* @param subpath
48+
*/
49+
get<S>(subpath: Path): ReadonlyDeep<S> | undefined;
50+
get(): ReadonlyDeep<T> | undefined;
51+
52+
getCollection(collectionName: string): Collection<JSONObject>;
53+
54+
/**
55+
* Gets a shallow copy of the value located at this model's path or a relative
56+
* subpath.
57+
*
58+
* If no value exists at the path, this returns `undefined`.
59+
*
60+
* @param subpath
61+
*/
62+
getCopy<S>(subpath: Path): ShallowCopiedValue<S> | undefined;
63+
getCopy(): ShallowCopiedValue<T> | undefined;
64+
65+
/**
66+
* Gets a deep copy of the value located at this model's path or a relative
67+
* subpath.
68+
*
69+
* If no value exists at the path, this returns `undefined`.
70+
*
71+
* @param subpath
72+
*/
73+
getDeepCopy<S>(subpath: Path): S | undefined;
74+
getDeepCopy(): T | undefined;
75+
2676
getDoc(collecitonName: string, id: string): any | undefined;
2777
getOrCreateCollection(name: string): Collection;
2878
getOrCreateDoc(collectionName: string, id: string, data: any);
@@ -47,28 +97,28 @@ Model.prototype.getDoc = function(collectionName, id) {
4797
return collection && collection.docs[id];
4898
};
4999

50-
Model.prototype.get = function(subpath) {
100+
Model.prototype.get = function<S>(subpath?: Path) {
51101
var segments = this._splitPath(subpath);
52-
return this._get(segments);
102+
return this._get(segments) as ReadonlyDeep<S>;
53103
};
54104

55105
Model.prototype._get = function(segments) {
56106
return util.lookup(segments, this.root.data);
57107
};
58108

59-
Model.prototype.getCopy = function(subpath) {
109+
Model.prototype.getCopy = function<S>(subpath?: Path) {
60110
var segments = this._splitPath(subpath);
61-
return this._getCopy(segments);
111+
return this._getCopy(segments) as ReadonlyDeep<S>;
62112
};
63113

64114
Model.prototype._getCopy = function(segments) {
65115
var value = this._get(segments);
66116
return util.copy(value);
67117
};
68118

69-
Model.prototype.getDeepCopy = function(subpath) {
119+
Model.prototype.getDeepCopy = function<S>(subpath?: Path) {
70120
var segments = this._splitPath(subpath);
71-
return this._getDeepCopy(segments);
121+
return this._getDeepCopy(segments) as S;
72122
};
73123

74124
Model.prototype._getDeepCopy = function(segments) {
@@ -135,12 +185,12 @@ Model.prototype.destroy = function(subpath) {
135185
}
136186
};
137187

138-
export class Collection {
188+
export class Collection<T extends JSONObject = {}> {
139189
model: RootModel;
140190
name: string;
141191
size: number;
142192
docs: DocMap;
143-
data: CollectionData;
193+
data: CollectionData<T>;
144194
Doc: typeof Doc;
145195

146196
constructor(model: RootModel, name: string, docClass: typeof Doc) {
@@ -149,7 +199,7 @@ export class Collection {
149199
this.Doc = docClass;
150200
this.size = 0;
151201
this.docs = new DocMap();
152-
this.data = model.data[name] = new CollectionData();
202+
this.data = model.data[name] = new CollectionData<T>();
153203
}
154204

155205
/**

src/util.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ export function equalsNaN(x) {
112112
return x !== x;
113113
}
114114

115-
export function isArrayIndex(segment) {
115+
export function isArrayIndex(segment: string): boolean {
116116
return (/^[0-9]+$/).test(segment);
117117
}
118118

119-
export function lookup(segments, value) {
119+
export function lookup(segments: string[], value: unknown): unknown {
120120
if (!segments) return value;
121121

122122
for (var i = 0, len = segments.length; i < len; i++) {
@@ -126,14 +126,14 @@ export function lookup(segments, value) {
126126
return value;
127127
}
128128

129-
export function mayImpactAny(segmentsList, testSegments) {
129+
export function mayImpactAny(segmentsList: string[][], testSegments: string[]) {
130130
for (var i = 0, len = segmentsList.length; i < len; i++) {
131131
if (mayImpact(segmentsList[i], testSegments)) return true;
132132
}
133133
return false;
134134
}
135135

136-
export function mayImpact(segments, testSegments) {
136+
export function mayImpact(segments: string[], testSegments: string[]): boolean {
137137
var len = Math.min(segments.length, testSegments.length);
138138
for (var i = 0; i < len; i++) {
139139
if (segments[i] !== testSegments[i]) return false;
@@ -186,13 +186,13 @@ export function serverRequire(module, id) {
186186
return module.require(id);
187187
}
188188

189-
export function serverUse(module, id, options) {
189+
export function serverUse(module, id: string, options?: unknown) {
190190
if (!isServer) return this;
191191
var plugin = module.require(id);
192192
return this.use(plugin, options);
193193
}
194194

195-
export function use(plugin, options) {
195+
export function use(plugin, options?: unknown) {
196196
// Don't include a plugin more than once
197197
var plugins = this._plugins || (this._plugins = []);
198198
if (plugins.indexOf(plugin) === -1) {

0 commit comments

Comments
 (0)