From 816b948ab082caa4d4f1757119f535259eed4296 Mon Sep 17 00:00:00 2001 From: Rifa Achrinza <25147899+achrinza@users.noreply.github.com> Date: Sun, 12 Sep 2021 17:32:36 +0800 Subject: [PATCH] feat: add built-in model property types typdef Added typdef for: - Any - JSON - Text - GeoPoint - DateString Signed-off-by: Rifa Achrinza <25147899+achrinza@users.noreply.github.com> --- index.d.ts | 1 + types/__tests__/date-string.spec.ts | 12 +++ types/__tests__/geopoint.spec.ts | 25 ++++++ types/__tests__/types.spec.ts | 15 ++++ types/date-string.d.ts | 21 +++++ types/geo.d.ts | 122 ++++++++++++++++++++++++++++ types/types.d.ts | 66 +++++++++++++++ 7 files changed, 262 insertions(+) create mode 100644 types/__tests__/date-string.spec.ts create mode 100644 types/__tests__/geopoint.spec.ts create mode 100644 types/__tests__/types.spec.ts create mode 100644 types/date-string.d.ts create mode 100644 types/geo.d.ts create mode 100644 types/types.d.ts diff --git a/index.d.ts b/index.d.ts index 641ee8be..3e6abc20 100644 --- a/index.d.ts +++ b/index.d.ts @@ -34,3 +34,4 @@ export * from './types/observer-mixin'; export * from './types/validation-mixin'; export * from './types/inclusion-mixin'; export * from './types/connector'; +export * from './types/geo'; diff --git a/types/__tests__/date-string.spec.ts b/types/__tests__/date-string.spec.ts new file mode 100644 index 00000000..138ffc97 --- /dev/null +++ b/types/__tests__/date-string.spec.ts @@ -0,0 +1,12 @@ +import { inspect } from 'util'; +import {DateString} from '../date-string'; + +let stringTypeGuard: string; + +const dateString = new DateString('2020-01-01'); +DateString('2020-01-01'); +DateString(dateString); +stringTypeGuard = dateString.toJSON().when; +stringTypeGuard = dateString.toString(); +stringTypeGuard = dateString.inspect(); +stringTypeGuard = dateString[inspect.custom](); diff --git a/types/__tests__/geopoint.spec.ts b/types/__tests__/geopoint.spec.ts new file mode 100644 index 00000000..6b434fbc --- /dev/null +++ b/types/__tests__/geopoint.spec.ts @@ -0,0 +1,25 @@ +import {GeoDistanceUnit, GeoPoint, filter, nearFilter} from '../geo'; + +let numberTypeGuard: number; + +new GeoPoint(123, 456); +new GeoPoint('123', 456); +new GeoPoint(123, '456'); +new GeoPoint('123', '456'); + +new GeoPoint([123, 456]); +new GeoPoint(['123', '456']); +new GeoPoint(['123', 456]); +new GeoPoint([123, '456']); + +new GeoPoint({lat: 123, lng: 456}); +new GeoPoint({lat: '123', lng: 456}) +new GeoPoint({lat: 123, lng: '456'}) +new GeoPoint({lat: '123', lng: '456'}); + +numberTypeGuard = GeoPoint.distanceBetwen([123, 456], [123, 456]); +numberTypeGuard = GeoPoint.distanceBetwen([123, 456], [123, 456], {type: GeoDistanceUnit.degrees}); + +const geoPoint = new GeoPoint(123, 456); +numberTypeGuard = geoPoint.distanceTo([123, 456]) +numberTypeGuard = geoPoint.distanceTo([123, 456], {type: GeoDistanceUnit.degrees}); diff --git a/types/__tests__/types.spec.ts b/types/__tests__/types.spec.ts new file mode 100644 index 00000000..9650a944 --- /dev/null +++ b/types/__tests__/types.spec.ts @@ -0,0 +1,15 @@ +import * as buildModelTypes from '../types'; +import {ModelTypes, Type, Types} from '../types'; + +let stringTypeGuard: string; +let voidTypeGuard: void; +let jsonTypeGuard: Types.JSON; + +stringTypeGuard = Types.JSON('arbitrary value'); +voidTypeGuard = Types.JSON(new Types.JSON('test')); +jsonTypeGuard = new Types.JSON('test'); +const modelTypes: ModelTypes = {} +buildModelTypes(modelTypes); +voidTypeGuard = modelTypes.registerType({} as Type); +voidTypeGuard = modelTypes.registerType({} as Type, ['custom name 1']); +modelTypes.schemaTypes; diff --git a/types/date-string.d.ts b/types/date-string.d.ts new file mode 100644 index 00000000..542664ca --- /dev/null +++ b/types/date-string.d.ts @@ -0,0 +1,21 @@ +import {inspect} from 'util'; + +// @achrinza: One of the limitations of these definitions is that the class instance +// isn't callable; Hence, changing the `value` class member must be done +// directly. This is a TypeScript limitation as class constructors cannot +// have a custom return value. +export function DateString(value: DateString | string): DateString; +export class DateString { + private _when: string; + private _date: Date; + + get when(): string; + set when(val: string); + + constructor(value: string); + + toString(): DateString['when']; + toJSON(): {when: DateString['when']}; + inspect(): string; + [inspect.custom]: DateString['inspect']; +} diff --git a/types/geo.d.ts b/types/geo.d.ts new file mode 100644 index 00000000..6aa3ea08 --- /dev/null +++ b/types/geo.d.ts @@ -0,0 +1,122 @@ +import { Where } from "./query"; + +export function nearFilter(where: Where): false | GeoPointFilter[]; + +export function filter(rawResults: GeoPointRawResult[], filters: GeoPointFilter[]): GeoPointFilter; + +export type GeoPointFilter = { + near: GeoPoint | ConstructorParameters; + maxDistance: number; + minDistance: number; + unit: GeoDistanceUnit; + mongoKey: string; + key: string; +} + +export type GeoPointRawResult = { + [key: string]: { + lat: number; + lng: number; + } +} + +export class GeoPoint { + lat: number; + lng: number; + + /** + * + * @example + * ```typescript + * new GeoPoint({ + * lat: 145, + * lng: 96, + * }); + * ``` + * + * @example + * ```typescript + * new GeoPoint({ + * lat: '145', + * lng: '96', + * }); + * ``` + * + * @param data + */ + constructor(data: { + lat: string | number, + lng: string | number, + }) + + /** + * @example + * ```typescript + * new GeoPoint('145,96'); + * ``` + * + * @example + * ```typescript + * new GeoPoint('145 , 96'); + * ``` + * + * @param data + */ + constructor(data: `${number},${number}`) + + /** + * @example + * ```typescript + * new GeoPoint([145, 96]); + * ``` + * + * @example + * ```typescript + * new GeoPoint(['145', '96']); + * ``` + * + * @param data + */ + constructor(data: [string | number, string | number]) + + /** + * @example + * ```typescript + * new GeoPoint(145, 96); + * ``` + * + * @example + * ```typescript + * new GeoPoint('145', '96'); + * ``` + * + * @param data + */ + constructor(lat: string | number, lng: string | number) + + static distanceBetwen( + a: GeoPoint | ConstructorParameters, + b: GeoPoint | ConstructorParameters, + options?: GeoDistanceOptions, + ): number; + + distanceTo( + point: GeoPoint | ConstructorParameters, + options?: GeoDistanceOptions, + ): number; + + toString(): `$${number},${number}`; +} + +export type GeoDistanceOptions = { + type: GeoDistanceUnit; +} + +export enum GeoDistanceUnit { + 'kilometers', + 'meters', + 'miles', + 'feet', + 'radians', + 'degrees', +} diff --git a/types/types.d.ts b/types/types.d.ts new file mode 100644 index 00000000..5ac8fc39 --- /dev/null +++ b/types/types.d.ts @@ -0,0 +1,66 @@ +import {DateString} from './date-string'; +import {GeoPoint} from './geo'; + +// @achrinza: The return value is a hack to inform TypeScript of function parameter mutations. +// see: https://github.com/microsoft/TypeScript/issues/22865#issuecomment-725015710 +declare function registerModelTypes(modelTypes: registerModelTypes.ModelTypes): asserts modelTypes is registerModelTypes.BuiltModelTypes; + +declare namespace registerModelTypes { + // @achrinza: One of the limitations of these definitions is that the class instance + // isn't callable; Hence, changing the `value` class member must be done + // directly. This is a TypeScript limitation as class constructors cannot + // have a custom return value. + namespace Types { + function Text(value: T): T extends Text ? void : T; + class Text implements Type { + value: Text; + constructor(value: Text); + toJSON(): Text; + toObject(): Text; + } + + function JSON(value: T): T extends JSON ? void : T; + class JSON implements Type { + value: unknown; + constructor(value: unknown) + toJSON(): unknown; + toObject(): unknown; + } + + + function Any(value: T): T extends Any ? void : T; + class Any implements Type { + value: unknown; + constructor(value: unknown); + toJSON(): unknown; + toObject(): unknown; + } + } + + interface ModelTypes { + [type: string]: Type | unknown; + } + + interface Type { + value: unknown; + toJSON(): unknown; + toObject(): unknown; + } + + interface BuiltModelTypes extends ModelTypes { + schemaTypes: Record & { + 'String': String; + 'Number': Number; + 'Date': Date + 'DateString': DateString + 'Binary': Buffer; + 'Buffer': Buffer; + 'Array': Array; + 'Object': Object; + 'GeoPoint': GeoPoint + }; + registerType: (type: Type, names?: string[]) => void; + } + +} +export = registerModelTypes;