Skip to content

Commit

Permalink
Better typing
Browse files Browse the repository at this point in the history
  • Loading branch information
j3lte committed Apr 13, 2023
1 parent f16d635 commit c09314d
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 86 deletions.
47 changes: 29 additions & 18 deletions src/Field.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import { DataType } from "./types.ts";
import type { Field, FieldImpl } from "./types.ts";
import { DataType, FieldImpl } from "./types.ts";

export const testFieldImpl = (field: FieldImpl | null, ...types: DataType[]): boolean =>
field === null || types.includes(field.type);
field === null || field?.type === DataType._Unknown || types.includes(field.type);

export const getFieldName = (field: string | FieldImpl): string => {
return typeof field === "string" ? field : field.name;
};
export class FieldObject<T extends DataType> {
readonly name: string;
readonly type: T;

constructor(name: string, type: T) {
this.name = name;
this.type = type;
}

toString(): string {
return this.name;
}
}

function Field(name: string): FieldObject<DataType._Unknown>;
function Field<T extends DataType>(name: string, type: T): FieldObject<T>;
function Field<T extends DataType>(name: string, type?: T): FieldObject<T | DataType._Unknown> {
return new FieldObject(name, type ?? DataType._Unknown);
}

export { Field };

export const SystemFields: {
Id: Field<DataType.RowIdentifier>;
CreatedAt: Field<DataType.FixedTimestamp>;
UpdatedAt: Field<DataType.FixedTimestamp>;
Id: FieldObject<DataType.RowIdentifier>;
CreatedAt: FieldObject<DataType.FixedTimestamp>;
UpdatedAt: FieldObject<DataType.FixedTimestamp>;
} = {
/** System field: **:id**, Only works in 2.1 */
Id: {
name: ":id",
type: DataType.RowIdentifier,
},
Id: Field(":id", DataType.RowIdentifier),
/** System field: **:created_at**, Only works in 2.1 */
CreatedAt: {
name: ":created_at",
type: DataType.FixedTimestamp,
},
CreatedAt: Field(":created_at", DataType.FixedTimestamp),
/** System field: **:updated_at**, Only works in 2.1 */
UpdatedAt: {
name: ":updated_at",
type: DataType.FixedTimestamp,
},
UpdatedAt: Field(":updated_at", DataType.FixedTimestamp),
};
28 changes: 17 additions & 11 deletions src/Select.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Field, FieldImpl } from "./types.ts";
import type { FieldImpl } from "./types.ts";
import { DataType } from "./types.ts";

import { getFieldName, testFieldImpl } from "./Field.ts";
import { FieldObject, getFieldName, testFieldImpl } from "./Field.ts";
import { SelectFunction, SelectImpl } from "./SelectImpl.ts";

/**
Expand Down Expand Up @@ -38,7 +38,10 @@ function SelectMultipleFunc(
*/
export function SelectGreatest(
...fields: Array<
string | Field<DataType.Text> | Field<DataType.Number> | Field<DataType.FloatingTimestamp>
| string
| FieldObject<DataType.Text>
| FieldObject<DataType.Number>
| FieldObject<DataType.FloatingTimestamp>
>
): SelectImpl {
return SelectMultipleFunc(
Expand All @@ -56,7 +59,10 @@ export function SelectGreatest(
*/
export function SelectLeast(
...fields: Array<
string | Field<DataType.Text> | Field<DataType.Number> | Field<DataType.FloatingTimestamp>
| string
| FieldObject<DataType.Text>
| FieldObject<DataType.Number>
| FieldObject<DataType.FloatingTimestamp>
>
): SelectImpl {
return SelectMultipleFunc(
Expand All @@ -73,8 +79,8 @@ export function SelectLeast(
* Docs: https://dev.socrata.com/docs/functions/regr_intercept.html
*/
export function SelectRegrIntercept(
xField: string | Field<DataType.Number>,
yField: string | Field<DataType.Number>,
xField: string | FieldObject<DataType.Number>,
yField: string | FieldObject<DataType.Number>,
): SelectImpl {
return SelectMultipleFunc(
SelectFunction.RegrIntercept,
Expand All @@ -91,8 +97,8 @@ export function SelectRegrIntercept(
* Docs: https://dev.socrata.com/docs/functions/regr_r2.html
*/
export function SelectRegrR2(
xField: string | Field<DataType.Number>,
yField: string | Field<DataType.Number>,
xField: string | FieldObject<DataType.Number>,
yField: string | FieldObject<DataType.Number>,
): SelectImpl {
return SelectMultipleFunc(
SelectFunction.RegrR2,
Expand All @@ -109,8 +115,8 @@ export function SelectRegrR2(
* Docs: https://dev.socrata.com/docs/functions/regr_slope.html
*/
export function SelectRegrSlope(
xField: string | Field<DataType.Number>,
yField: string | Field<DataType.Number>,
xField: string | FieldObject<DataType.Number>,
yField: string | FieldObject<DataType.Number>,
): SelectImpl {
return SelectMultipleFunc(
SelectFunction.RegrSlope,
Expand All @@ -124,7 +130,7 @@ export function SelectRegrSlope(
function Select(): SelectImpl<DataType._Unknown>;
function Select<T extends string>(field: T): SelectImpl<DataType._Unknown>;
function Select<T extends FieldImpl>(field: T): SelectImpl<T["type"]>;
function Select<T extends DataType>(field: Field<T>): SelectImpl<T>;
function Select<T extends DataType>(field: FieldObject<T>): SelectImpl<T>;
function Select(field?: string | FieldImpl): SelectImpl<DataType._Unknown> {
return new SelectImpl(field);
}
Expand Down
38 changes: 19 additions & 19 deletions src/Where.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// TODO(@j3lte) - Fix the 'any' types in this file
// deno-lint-ignore-file no-explicit-any

import type { Field, FieldImpl } from "./types.ts";
import type { FieldImpl } from "./types.ts";

import { replaceParams, SupportTypeElement } from "./utils/param.ts";
import { getFieldName } from "./Field.ts";
import { FieldObject, getFieldName } from "./Field.ts";
import { DataType } from "./types.ts";

type BasicType = Exclude<SupportTypeElement, null | undefined | boolean>;
Expand Down Expand Up @@ -123,10 +123,10 @@ export class Where {
static between(
field:
| string
| Field<DataType.Number>
| Field<DataType.FloatingTimestamp>
| Field<DataType.FixedTimestamp>
| Field<DataType.Text>,
| FieldObject<DataType.Number>
| FieldObject<DataType.FloatingTimestamp>
| FieldObject<DataType.FixedTimestamp>
| FieldObject<DataType.Text>,
startValue: any,
endValue: any,
): Where {
Expand All @@ -141,10 +141,10 @@ export class Where {
static notBetween(
field:
| string
| Field<DataType.Number>
| Field<DataType.FloatingTimestamp>
| Field<DataType.FixedTimestamp>
| Field<DataType.Text>,
| FieldObject<DataType.Number>
| FieldObject<DataType.FloatingTimestamp>
| FieldObject<DataType.FixedTimestamp>
| FieldObject<DataType.Text>,
startValue: any,
endValue: any,
): Where {
Expand Down Expand Up @@ -214,7 +214,7 @@ export class Where {
* @param lonSE The longitude of the southeast corner of the box
*/
static withinBox(
field: string | Field<DataType.Location>,
field: string | FieldObject<DataType.Location>,
latNW: number,
lonNW: number,
latSE: number,
Expand All @@ -238,7 +238,7 @@ export class Where {
* @param radius The radius of the circle in meters
*/
static withinCircle(
field: string | Field<DataType.Location>,
field: string | FieldObject<DataType.Location>,
lat: number,
lon: number,
radius: number,
Expand All @@ -257,7 +257,7 @@ export class Where {
* @param field The field to search
* @param value The value to search for
*/
static startsWith(field: string | Field<DataType.Text>, value: string): Where {
static startsWith(field: string | FieldObject<DataType.Text>, value: string): Where {
return this.expr("starts_with(??, ?)", getFieldName(field), value);
}

Expand All @@ -274,12 +274,12 @@ export class Where {
static intersects(
field:
| string
| Field<DataType.Point>
| Field<DataType.Polygon>
| Field<DataType.Line>
| Field<DataType.MultiPoint>
| Field<DataType.MultiLine>
| Field<DataType.MultiPolygon>,
| FieldObject<DataType.Point>
| FieldObject<DataType.Polygon>
| FieldObject<DataType.Line>
| FieldObject<DataType.MultiPoint>
| FieldObject<DataType.MultiLine>
| FieldObject<DataType.MultiPolygon>,
value: string,
): Where {
return this.expr("intersects(??, ?)", getFieldName(field), value);
Expand Down
4 changes: 2 additions & 2 deletions src/mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { AuthOpts, DataResponse, Field, FieldImpl, Options, QueryObj } from "./types.ts";
export type { AuthOpts, DataResponse, FieldImpl, Options, QueryObj } from "./types.ts";
export { DataType } from "./types.ts";

export { createQueryWithDataset, SodaQuery } from "./Query.ts";
Expand All @@ -20,4 +20,4 @@ export { Order } from "./Order.ts";

export { expr } from "./utils/expr.ts";

export { SystemFields } from "./Field.ts";
export { Field, FieldObject, SystemFields } from "./Field.ts";
43 changes: 23 additions & 20 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FieldObject } from "./Field.ts";
/**
* The different types of data that can be used in a field
*/
Expand Down Expand Up @@ -34,6 +35,8 @@ export enum DataType {
_Unknown = "_unknown",
}

export type FieldImpl = FieldObject<DataType>;

export interface Options {
/** Strict mode. If enabled, this prevents the Query from changing the dataset ID after it has been set once */
strict?: boolean;
Expand Down Expand Up @@ -83,27 +86,27 @@ export interface AuthOpts {
accessToken?: string;
}

export type Field<T> = {
name: string;
type: T;
};
// export type Field<T> = {
// name: string;
// type: T;
// };

export type FieldImpl =
| Field<DataType.Checkbox>
| Field<DataType.FixedTimestamp>
| Field<DataType.FloatingTimestamp>
| Field<DataType.Line>
| Field<DataType.Location>
| Field<DataType.MultiLine>
| Field<DataType.MultiPoint>
| Field<DataType.MultiPolygon>
| Field<DataType.Number>
| Field<DataType.Point>
| Field<DataType.Polygon>
| Field<DataType.Text>
| Field<DataType.URL>
| Field<DataType.RowIdentifier>
| Field<DataType._Unknown>;
// export type FieldImpl =
// | Field<DataType.Checkbox>
// | Field<DataType.FixedTimestamp>
// | Field<DataType.FloatingTimestamp>
// | Field<DataType.Line>
// | Field<DataType.Location>
// | Field<DataType.MultiLine>
// | Field<DataType.MultiPoint>
// | Field<DataType.MultiPolygon>
// | Field<DataType.Number>
// | Field<DataType.Point>
// | Field<DataType.Polygon>
// | Field<DataType.Text>
// | Field<DataType.URL>
// | Field<DataType.RowIdentifier>
// | Field<DataType._Unknown>;

export type QueryObj = Record<string, string | number | boolean>;
export type DataResponse<T> = Promise<{ error: Error | null; status: number; data: T }>;
24 changes: 14 additions & 10 deletions test/Field.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import type { Field } from "../src/types.ts";
// import type { Field } from "../src/types.ts";

import { assertEquals } from "../dev_deps.ts";
import { getFieldName, testFieldImpl } from "../src/Field.ts";
import { Field, getFieldName, testFieldImpl } from "../src/Field.ts";
import { DataType } from "../src/types.ts";

Deno.test("(Field.)testFieldImpl", () => {
const field: Field<DataType.Checkbox> = {
name: "test",
type: DataType.Checkbox,
};
const field = Field("test", DataType.Checkbox);
assertEquals(testFieldImpl(null), true);
assertEquals(testFieldImpl(null, DataType.Checkbox), true);
assertEquals(testFieldImpl(field, DataType.Checkbox), true);
Expand All @@ -19,10 +16,17 @@ Deno.test("(Field.)testFieldImpl", () => {
Deno.test("(Field.)getFieldName", () => {
assertEquals(getFieldName("test"), "test");
assertEquals(
getFieldName({
name: "field",
type: DataType.Checkbox,
}),
getFieldName(Field("field", DataType.Checkbox)),
"field",
);
});

Deno.test("(Field.)Field", () => {
const field = Field("test", DataType.Checkbox);
assertEquals(field.name, "test");
assertEquals(field.type, DataType.Checkbox);
assertEquals(field.toString(), "test");

const field2 = Field("test2");
assertEquals(field2.type, DataType._Unknown);
});
8 changes: 2 additions & 6 deletions test/Select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@ import {
SelectRegrSlope,
} from "../src/Select.ts";
import { DataType } from "../src/types.ts";
import type { Field } from "../src/types.ts";
import { Field } from "../src/Field.ts";

const createField = <T>(type: T, name = "test"): Field<T> =>
({
name,
type,
}) as const;
const createField = <T extends DataType>(type: T, name = "test") => Field(name, type);

Deno.test("Select (empty)", () => {
const select = Select();
Expand Down

0 comments on commit c09314d

Please sign in to comment.