aboutsummaryrefslogtreecommitdiff
path: root/src/fields/List.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields/List.ts')
-rw-r--r--src/fields/List.ts104
1 files changed, 53 insertions, 51 deletions
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 38c47d546..22bbcb9ab 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -2,33 +2,33 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { alias, list as serializrList, serializable } from 'serializr';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper';
-import { Field, FieldType, StrListCast } from './Doc';
+import { Doc, Field, FieldType, ObjGetRefFields, StrListCast } from './Doc';
import { FieldTuples, Self, SelfProxy } from './DocSymbols';
import { Copy, FieldChanged, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
-import { ObjGetRefFields, ObjectField } from './ObjectField';
+import { ObjectField } from './ObjectField';
import { ProxyField } from './Proxy';
import { RefField } from './RefField';
import { containedFieldChangedHandler, deleteProperty, getter, setter } from './util';
function toObjectField(field: FieldType) {
- return field instanceof RefField ? new ProxyField(field) : field;
+ return field instanceof Doc ? new ProxyField(field) : field;
}
-function toRealField(field: FieldType) {
+function toRealField(field: FieldType | undefined) {
return field instanceof ProxyField ? field.value : field;
}
-type StoredType<T extends FieldType> = T extends RefField ? ProxyField<T> : T;
+type StoredType<T extends FieldType> = T extends Doc ? ProxyField<T> : T;
export const ListFieldName = 'fields';
@Deserializable('list')
-class ListImpl<T extends FieldType> extends ObjectField {
- static listHandlers: any = {
+export class ListImpl<T extends FieldType> extends ObjectField {
+ static listHandlers = {
/// Mutator methods
- copyWithin() {
+ copyWithin: function (this: ListImpl<FieldType>) {
throw new Error('copyWithin not supported yet');
},
- fill(value: any, start?: number, end?: number) {
+ fill: function (this: ListImpl<FieldType>, value: FieldType, start?: number, end?: number) {
if (value instanceof RefField) {
throw new Error('fill with RefFields not supported yet');
}
@@ -36,12 +36,12 @@ class ListImpl<T extends FieldType> extends ObjectField {
this[SelfProxy][FieldChanged]?.();
return res;
},
- pop(): any {
+ pop: function (this: ListImpl<FieldType>): FieldType {
const field = toRealField(this[Self].__fieldTuples.pop());
this[SelfProxy][FieldChanged]?.();
return field;
},
- push: action(function (this: ListImpl<any>, ...itemsIn: any[]) {
+ push: action(function (this: ListImpl<FieldType>, ...itemsIn: FieldType[]) {
const items = itemsIn.map(toObjectField);
const list = this[Self];
@@ -58,27 +58,27 @@ class ListImpl<T extends FieldType> extends ObjectField {
this[SelfProxy][FieldChanged]?.({ op: '$addToSet', items, length: length + items.length });
return res;
}),
- reverse() {
+ reverse: function (this: ListImpl<FieldType>) {
const res = this[Self].__fieldTuples.reverse();
this[SelfProxy][FieldChanged]?.();
return res;
},
- shift() {
+ shift: function (this: ListImpl<FieldType>) {
const res = toRealField(this[Self].__fieldTuples.shift());
this[SelfProxy][FieldChanged]?.();
return res;
},
- sort(cmpFunc: any) {
+ sort: function (this: ListImpl<FieldType>, cmpFunc: (first: FieldType | undefined, second: FieldType | undefined) => number) {
this[Self].__realFields; // coerce retrieving entire array
- const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
+ const res = this[Self].__fieldTuples.sort(cmpFunc ? (first: FieldType, second: FieldType) => cmpFunc(toRealField(first), toRealField(second)) : undefined);
this[SelfProxy][FieldChanged]?.();
return res;
},
- splice: action(function (this: any, start: number, deleteCount: number, ...itemsIn: any[]) {
+ splice: action(function (this: ListImpl<FieldType>, start: number, deleteCount: number, ...itemsIn: FieldType[]) {
this[Self].__realFields; // coerce retrieving entire array
const items = itemsIn.map(toObjectField);
const list = this[Self];
- const removed = list.__fieldTuples.filter((item: any, i: number) => i >= start && i < start + deleteCount);
+ const removed = list.__fieldTuples.filter((item: FieldType, i: number) => i >= start && i < start + deleteCount);
for (let i = 0; i < items.length; i++) {
const item = items[i];
// TODO Error checking to make sure parent doesn't already exist
@@ -88,7 +88,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
item[FieldChanged] = containedFieldChangedHandler(this, i + start, item);
}
}
- const hintArray: { val: any; index: number }[] = [];
+ const hintArray: { val: FieldType; index: number }[] = [];
for (let i = start; i < start + deleteCount; i++) {
hintArray.push({ val: list.__fieldTuples[i], index: i });
}
@@ -104,7 +104,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
);
return res.map(toRealField);
}),
- unshift(...itemsIn: any[]) {
+ unshift: function (this: ListImpl<FieldType>, ...itemsIn: FieldType[]) {
const items = itemsIn.map(toObjectField);
const list = this[Self];
for (let i = 0; i < items.length; i++) {
@@ -121,108 +121,108 @@ class ListImpl<T extends FieldType> extends ObjectField {
return res;
},
/// Accessor methods
- concat: action(function (this: any, ...items: any[]) {
+ concat: action(function (this: ListImpl<FieldType>, ...items: FieldType[]) {
this[Self].__realFields;
return this[Self].__fieldTuples.map(toRealField).concat(...items);
}),
- includes(valueToFind: any, fromIndex: number) {
+ includes: function (this: ListImpl<FieldType>, valueToFind: FieldType, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.includes(valueToFind, fromIndex);
}
return this[Self].__fieldTuples.includes(valueToFind, fromIndex);
},
- indexOf(valueToFind: any, fromIndex: number) {
+ indexOf: function (this: ListImpl<FieldType>, valueToFind: FieldType, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.indexOf(valueToFind, fromIndex);
}
return this[Self].__fieldTuples.indexOf(valueToFind, fromIndex);
},
- join(separator: any) {
+ join: function (this: ListImpl<FieldType>, separator: string) {
this[Self].__realFields;
return this[Self].__fieldTuples.map(toRealField).join(separator);
},
- lastElement() {
+ lastElement: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.lastElement();
},
- lastIndexOf(valueToFind: any, fromIndex: number) {
+ lastIndexOf: function (this: ListImpl<FieldType>, valueToFind: FieldType, fromIndex: number) {
if (valueToFind instanceof RefField) {
return this[Self].__realFields.lastIndexOf(valueToFind, fromIndex);
}
return this[Self].__fieldTuples.lastIndexOf(valueToFind, fromIndex);
},
- slice(begin: number, end: number) {
+ slice: function (this: ListImpl<FieldType>, begin: number, end: number) {
this[Self].__realFields;
return this[Self].__fieldTuples.slice(begin, end).map(toRealField);
},
/// Iteration methods
- entries() {
+ entries: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.entries();
},
- every(callback: any, thisArg: any) {
+ every: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => unknown, thisArg: unknown) {
return this[Self].__realFields.every(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- filter(callback: any, thisArg: any) {
+ filter: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => FieldType[], thisArg: unknown) {
return this[Self].__realFields.filter(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- find(callback: any, thisArg: any) {
+ find: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, obj: FieldType[]) => FieldType, thisArg: unknown) {
return this[Self].__realFields.find(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- findIndex(callback: any, thisArg: any) {
+ findIndex: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, obj: FieldType[]) => number, thisArg: unknown) {
return this[Self].__realFields.findIndex(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- forEach(callback: any, thisArg: any) {
+ forEach: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => void, thisArg: unknown) {
return this[Self].__realFields.forEach(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- map(callback: any, thisArg: any) {
+ map: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => unknown, thisArg: unknown) {
return this[Self].__realFields.map(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- reduce(callback: any, initialValue: any) {
+ reduce: function (this: ListImpl<FieldType>, callback: (previousValue: unknown, currentValue: FieldType, currentIndex: number, array: FieldType[]) => unknown, initialValue: unknown) {
return this[Self].__realFields.reduce(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
- reduceRight(callback: any, initialValue: any) {
+ reduceRight: function (this: ListImpl<FieldType>, callback: (previousValue: unknown, currentValue: FieldType, currentIndex: number, array: FieldType[]) => unknown, initialValue: unknown) {
return this[Self].__realFields.reduceRight(callback, initialValue);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue);
},
- some(callback: any, thisArg: any) {
+ some: function (this: ListImpl<FieldType>, callback: (value: FieldType, index: number, array: FieldType[]) => boolean, thisArg: unknown) {
return this[Self].__realFields.some(callback, thisArg);
// TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway.
// If we don't want to support the array parameter, we should use this version instead
// return this[Self].__fieldTuples.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg);
},
- values() {
+ values: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.values();
},
- [Symbol.iterator]() {
+ [Symbol.iterator]: function (this: ListImpl<FieldType>) {
return this[Self].__realFields.values();
},
};
- static listGetter(target: any, prop: string | symbol, receiver: any): any {
+ static listGetter(target: ListImpl<FieldType>, prop: string | symbol, receiver: ListImpl<FieldType>): unknown {
if (Object.prototype.hasOwnProperty.call(ListImpl.listHandlers, prop)) {
- return ListImpl.listHandlers[prop];
+ return (ListImpl.listHandlers as { [key: string | symbol]: unknown })[prop];
}
return getter(target, prop, receiver);
}
@@ -251,7 +251,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
},
});
// eslint-disable-next-line no-use-before-define
- this[SelfProxy] = list as any as List<FieldType>; // bcz: ugh .. don't know how to convince typesecript that list is a List
+ this[SelfProxy] = list as unknown as List<FieldType>; // bcz: ugh .. don't know how to convince typesecript that list is a List
if (fields) {
this[SelfProxy].push(...fields);
}
@@ -260,18 +260,20 @@ class ListImpl<T extends FieldType> extends ObjectField {
}
[key: number]: T | (T extends RefField ? Promise<T> : never);
+ [key2: symbol]: unknown;
+ [key3: string]: unknown;
// this requests all ProxyFields at the same time to avoid the overhead
// of separate network requests and separate updates to the React dom.
@computed private get __realFields() {
- const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<RefField>);
+ const unrequested = this[FieldTuples].filter(f => f instanceof ProxyField && f.needsRequesting).map(f => f as ProxyField<Doc>);
// if we find any ProxyFields that don't have a current value, then
// start the server request for all of them
if (unrequested.length) {
const batchPromise = ObjGetRefFields(unrequested.map(p => p.fieldId));
// as soon as we get the fields from the server, set all the list values in one
// action to generate one React dom update.
- const allSetPromise = batchPromise.then(action(pfields => unrequested.map(toReq => toReq.setValue(pfields[toReq.fieldId]))));
+ const allSetPromise = batchPromise.then(action(pfields => unrequested.map(toReq => toReq.setValue(pfields.get(toReq.fieldId)))));
// we also have to mark all lists items with this promise so that any calls to them
// will await the batch request and return the requested field value.
unrequested.forEach(p => p.setExternalValuePromise(allSetPromise));
@@ -280,11 +282,11 @@ class ListImpl<T extends FieldType> extends ObjectField {
}
@serializable(alias(ListFieldName, serializrList(autoObject(), { afterDeserialize: afterDocDeserialize })))
- private get __fieldTuples() {
+ get __fieldTuples() {
return this[FieldTuples];
}
- private set __fieldTuples(value) {
+ set __fieldTuples(value) {
this[FieldTuples] = value;
Object.keys(value).forEach(key => {
const item = value[Number(key)];
@@ -297,7 +299,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
[Copy]() {
const copiedData = this[Self].__fieldTuples.map(f => (f instanceof ObjectField ? f[Copy]() : f));
- const deepCopy = new ListImpl<T>(copiedData as any);
+ const deepCopy = new ListImpl<T>(copiedData as T[]);
return deepCopy;
}
@@ -309,19 +311,19 @@ class ListImpl<T extends FieldType> extends ObjectField {
private [SelfProxy]: List<FieldType>; // also used in utils.ts even though it won't be found using find all references
[ToScriptString]() { return `new List(${this[ToJavascriptString]()})`; } // prettier-ignore
- [ToJavascriptString]() { return `[${(this as any).map((field: any) => Field.toScriptString(field))}]`; } // prettier-ignore
- [ToString]() { return `[${(this as any).map((field: any) => Field.toString(field))}]`; } // prettier-ignore
+ [ToJavascriptString]() { return `[${(this[FieldTuples]).map(field => Field.toScriptString(field))}]`; } // prettier-ignore
+ [ToString]() { return `[${(this[FieldTuples]).map(field => Field.toString(field))}]`; } // prettier-ignore
}
// declare List as a type so you can use it in type declarations, e.g., { l: List, ...}
export type List<T extends FieldType> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
-// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>()
+// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>() (since List<T> IS ListImpl<T>, we can safely cast the 'new' return value to return List<T>)
// eslint-disable-next-line no-redeclare
-export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as any;
+export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as unknown as { new <T extends FieldType>(fields?: T[]): List<T> };
ScriptingGlobals.add('List', List);
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function compareLists(l1: any, l2: any) {
+ScriptingGlobals.add(function compareLists(l1: List<FieldType>, l2: List<FieldType>) {
const L1 = StrListCast(l1);
const L2 = StrListCast(l2);
return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);