Skip to content
This repository was archived by the owner on Jan 2, 2024. It is now read-only.

Commit cca5047

Browse files
feat: typesafe query element functions (#23)
* chore(deps): bump wkt-io-ts and dev deps * feat: use generic functions for query elements BREAKING CHANGE: old `[Some]Term.get([PrimitiveCodec])!` now becomes `[SomeTerm]([PrimitiveCodec])` and is now properly type-checked
1 parent 0e4192a commit cca5047

File tree

4 files changed

+126
-74
lines changed

4 files changed

+126
-74
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@
111111
"io-ts": "^2.0.1",
112112
"io-ts-types": "^0.5.0",
113113
"monocle-ts": "^2.0.0",
114-
"wkt-io-ts": "^1.0.0"
114+
"wkt-io-ts": "^1.0.1"
115115
},
116116
"devDependencies": {
117-
"@holvonix-open/release-config-js": "^1.0.4",
117+
"@holvonix-open/release-config-js": "^1.0.6",
118118
"@types/mocha": "^5.2.5",
119119
"@types/nock": "^10.0.3",
120120
"@types/node": "^12.7.1",

src/types.ts

+113-61
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,20 @@ function forPrimitives<A, O = A, I = unknown>(
7575
return new Map(e);
7676
}
7777

78-
export const Literal = forPrimitives(<T extends PrimitiveC>(c: PrimitiveC) =>
78+
const literal = forPrimitives(<T extends PrimitiveC>(c: PrimitiveC) =>
7979
t.type({
8080
type: t.literal('literal'),
8181
value: c,
8282
})
8383
);
84+
export function Literal<T extends PrimitiveC>(
85+
c: T
86+
): t.Type<Literal<t.TypeOf<T>>> {
87+
return literal.get(c)! as t.Type<Literal<t.TypeOf<T>>>;
88+
}
89+
8490
export type SomeLiteral = t.Type<Literal<Primitive>>;
85-
export const AnyLiteral = t.union(Array.from(Literal).map(x => x[1]) as [
91+
export const AnyLiteral = t.union(Array.from(literal).map(x => x[1]) as [
8692
SomeLiteral,
8793
SomeLiteral,
8894
...SomeLiteral[]
@@ -120,7 +126,7 @@ function forRangedPrimitives<A, O, I>(
120126
return new Map(e);
121127
}
122128

123-
export const Range = forRangedPrimitives(<T extends RangedPrimitiveC>(c: T) =>
129+
const range = forRangedPrimitives(<T extends RangedPrimitiveC>(c: T) =>
124130
t.intersection([
125131
t.type({
126132
type: t.literal('range'),
@@ -134,8 +140,13 @@ export const Range = forRangedPrimitives(<T extends RangedPrimitiveC>(c: T) =>
134140
}),
135141
])
136142
);
143+
export function Range<T extends RangedPrimitiveC>(
144+
c: T
145+
): t.Type<Range<t.TypeOf<T>>> {
146+
return range.get(c)! as t.Type<Range<t.TypeOf<T>>>;
147+
}
137148
export type SomeRange = t.Type<Range<RangedPrimitive>>;
138-
export const AnyRange = t.union(Array.from(Range).map(x => x[1]) as [
149+
export const AnyRange = t.union(Array.from(range).map(x => x[1]) as [
139150
SomeRange,
140151
SomeRange,
141152
...SomeRange[]
@@ -171,33 +182,33 @@ export type TermValue<T extends Primitive> =
171182
| MaybeGlob<T>
172183
| MaybeRange<T>
173184
| NonPrimitiveTermValue<T>;
174-
export const TermValue = forPrimitives<TermValue<t.TypeOf<PrimitiveC>>>(
185+
const termValue = forPrimitives<TermValue<t.TypeOf<PrimitiveC>>>(
175186
<T extends PrimitiveC>(c: T): t.Type<TermValue<t.TypeOf<PrimitiveC>>> =>
176187
t.recursion(`TermValue<${c.name}>`, () => {
177-
let ret: t.Type<TermValue<t.TypeOf<PrimitiveC>>> | undefined = undefined;
188+
let ret: t.Type<TermValue<t.TypeOf<T>>> | undefined = undefined;
178189
if (IsGlobPrimitiveC(c)) {
179-
ret = Literal.get(Glob)! as t.Type<TermValue<t.TypeOf<PrimitiveC>>>;
190+
ret = Literal(Glob) as t.Type<TermValue<t.TypeOf<T>>>;
180191
}
181192
if (IsRangedPrimitiveC(c)) {
182-
ret = (ret ? t.union([ret, Range.get(c)!]) : Range.get(c)!) as t.Type<
183-
TermValue<t.TypeOf<PrimitiveC>>
193+
ret = (ret ? t.union([ret, Range(c)]) : Range(c)) as t.Type<
194+
TermValue<t.TypeOf<T>>
184195
>;
185196
}
186197
if (ret) {
187-
ret = t.union([
188-
Literal.get(c)!,
189-
NonPrimitiveTermValue.get(c)!,
190-
ret,
191-
]) as t.Type<TermValue<t.TypeOf<PrimitiveC>>>;
198+
ret = t.union([Literal(c), NonPrimitiveTermValue(c), ret]);
192199
} else {
193-
ret = t.union([
194-
Literal.get(c)!,
195-
NonPrimitiveTermValue.get(c)!,
196-
]) as t.Type<TermValue<t.TypeOf<PrimitiveC>>>;
200+
ret = t.union([Literal(c), NonPrimitiveTermValue(c)]) as t.Type<
201+
TermValue<t.TypeOf<T>>
202+
>;
197203
}
198-
return ret!;
204+
return ret! as t.Type<TermValue<t.TypeOf<PrimitiveC>>>;
199205
})
200206
);
207+
export function TermValue<T extends PrimitiveC>(
208+
c: T
209+
): t.Type<TermValue<t.TypeOf<T>>> {
210+
return termValue.get(c)! as t.Type<TermValue<t.TypeOf<T>>>;
211+
}
201212

202213
export interface AndBase<U> {
203214
type: 'and';
@@ -211,13 +222,18 @@ const AndBase = <U extends t.Any>(c: U) =>
211222
export interface And extends AndBase<Clause> {}
212223
export const And = t.recursion<And>('And', (): t.Type<And> => AndBase(Clause));
213224
export interface AndTerm<T extends Primitive> extends AndBase<TermValue<T>> {}
214-
export const AndTerm = forPrimitives(
225+
const andTerm = forPrimitives(
215226
<U extends PrimitiveC>(c: U): t.Type<AndTerm<t.TypeOf<U>>> =>
216227
t.recursion(
217228
`AndTerm<${c.name}>`,
218-
() => AndBase(TermValue.get(c)!) as t.Type<AndTerm<t.TypeOf<U>>>
229+
() => AndBase(TermValue(c)) as t.Type<AndTerm<t.TypeOf<U>>>
219230
)
220231
);
232+
export function AndTerm<T extends PrimitiveC>(
233+
c: T
234+
): t.Type<AndTerm<t.TypeOf<T>>> {
235+
return andTerm.get(c)! as t.Type<AndTerm<t.TypeOf<T>>>;
236+
}
221237

222238
export interface OrBase<U> {
223239
type: 'or';
@@ -231,13 +247,18 @@ const OrBase = <U extends t.Any>(c: U) =>
231247
export interface Or extends OrBase<Clause> {}
232248
export const Or = t.recursion<Or>('Or', (): t.Type<Or> => OrBase(Clause));
233249
export interface OrTerm<T extends Primitive> extends OrBase<TermValue<T>> {}
234-
export const OrTerm = forPrimitives(
250+
const orTerm = forPrimitives(
235251
<U extends PrimitiveC>(c: U): t.Type<OrTerm<t.TypeOf<U>>> =>
236252
t.recursion(
237253
`OrTerm<${c.name}>`,
238-
() => OrBase(TermValue.get(c)!) as t.Type<OrTerm<t.TypeOf<U>>>
254+
() => OrBase(TermValue(c)) as t.Type<OrTerm<t.TypeOf<U>>>
239255
)
240256
);
257+
export function OrTerm<T extends PrimitiveC>(
258+
c: T
259+
): t.Type<OrTerm<t.TypeOf<T>>> {
260+
return orTerm.get(c)! as t.Type<OrTerm<t.TypeOf<T>>>;
261+
}
241262

242263
export interface NotBase<U> {
243264
type: 'not';
@@ -251,13 +272,18 @@ const NotBase = <U extends t.Any>(c: U) =>
251272
export interface Not extends NotBase<Clause> {}
252273
export const Not = t.recursion<Not>('Not', (): t.Type<Not> => NotBase(Clause));
253274
export interface NotTerm<T extends Primitive> extends NotBase<TermValue<T>> {}
254-
export const NotTerm = forPrimitives(
275+
const notTerm = forPrimitives(
255276
<U extends PrimitiveC>(c: U): t.Type<NotTerm<t.TypeOf<U>>> =>
256277
t.recursion(
257278
`NotTerm<${c.name}>`,
258-
() => NotBase(TermValue.get(c)!) as t.Type<NotTerm<t.TypeOf<U>>>
279+
() => NotBase(TermValue(c)) as t.Type<NotTerm<t.TypeOf<U>>>
259280
)
260281
);
282+
export function NotTerm<T extends PrimitiveC>(
283+
c: T
284+
): t.Type<NotTerm<t.TypeOf<T>>> {
285+
return notTerm.get(c)! as t.Type<NotTerm<t.TypeOf<T>>>;
286+
}
261287

262288
export interface RequiredBase<U> {
263289
type: 'required';
@@ -275,13 +301,18 @@ export const Required = t.recursion<Required>(
275301
);
276302
export interface RequiredTerm<T extends Primitive>
277303
extends RequiredBase<TermValue<T>> {}
278-
export const RequiredTerm = forPrimitives(
304+
const requiredTerm = forPrimitives(
279305
<U extends PrimitiveC>(c: U): t.Type<RequiredTerm<t.TypeOf<U>>> =>
280306
t.recursion(
281307
`RequiredTerm<${c.name}>`,
282-
() => RequiredBase(TermValue.get(c)!) as t.Type<RequiredTerm<t.TypeOf<U>>>
308+
() => RequiredBase(TermValue(c)) as t.Type<RequiredTerm<t.TypeOf<U>>>
283309
)
284310
);
311+
export function RequiredTerm<T extends PrimitiveC>(
312+
c: T
313+
): t.Type<RequiredTerm<t.TypeOf<T>>> {
314+
return requiredTerm.get(c)! as t.Type<RequiredTerm<t.TypeOf<T>>>;
315+
}
285316

286317
export interface ProhibitedBase<U> {
287318
type: 'prohibited';
@@ -299,14 +330,18 @@ export const Prohibited = t.recursion<Prohibited>(
299330
);
300331
export interface ProhibitedTerm<T extends Primitive>
301332
extends ProhibitedBase<TermValue<T>> {}
302-
export const ProhibitedTerm = forPrimitives(
333+
const prohibitedTerm = forPrimitives(
303334
<U extends PrimitiveC>(c: U): t.Type<ProhibitedTerm<t.TypeOf<U>>> =>
304335
t.recursion(
305336
`ProhibitedTerm<${c.name}>`,
306-
() =>
307-
ProhibitedBase(TermValue.get(c)!) as t.Type<ProhibitedTerm<t.TypeOf<U>>>
337+
() => ProhibitedBase(TermValue(c)) as t.Type<ProhibitedTerm<t.TypeOf<U>>>
308338
)
309339
);
340+
export function ProhibitedTerm<T extends PrimitiveC>(
341+
c: T
342+
): t.Type<ProhibitedTerm<t.TypeOf<T>>> {
343+
return prohibitedTerm.get(c)! as t.Type<ProhibitedTerm<t.TypeOf<T>>>;
344+
}
310345

311346
export interface ConstantScore {
312347
type: 'constant';
@@ -329,35 +364,48 @@ export type NonPrimitiveTermValue<T extends Primitive> =
329364
| NotTerm<T>
330365
| RequiredTerm<T>
331366
| ProhibitedTerm<T>;
332-
export const NonPrimitiveTermValue = forPrimitives(
367+
const nonPrimitiveTermValue = forPrimitives(
333368
<T extends PrimitiveC>(c: T): t.Type<NonPrimitiveTermValue<t.TypeOf<T>>> =>
334369
t.recursion(
335370
`NonPrimitiveTermValue<${c.name}>`,
336371
() =>
337372
t.union([
338-
AndTerm.get(c)!,
339-
OrTerm.get(c)!,
340-
NotTerm.get(c)!,
341-
RequiredTerm.get(c)!,
342-
ProhibitedTerm.get(c)!,
373+
AndTerm(c),
374+
OrTerm(c),
375+
NotTerm(c),
376+
RequiredTerm(c),
377+
ProhibitedTerm(c),
343378
]) as t.Type<NonPrimitiveTermValue<t.TypeOf<T>>>
344379
)
345380
);
381+
export function NonPrimitiveTermValue<T extends PrimitiveC>(
382+
c: T
383+
): t.Type<NonPrimitiveTermValue<t.TypeOf<T>>> {
384+
return nonPrimitiveTermValue.get(c)! as t.Type<
385+
NonPrimitiveTermValue<t.TypeOf<T>>
386+
>;
387+
}
346388

347389
export interface NamedTerm<T extends Primitive> {
348390
type: 'namedterm';
349391
field: string;
350392
value: TermValue<T>;
351393
}
352-
export const NamedTerm = forPrimitives(<T extends PrimitiveC>(c: T) =>
353-
t.type({
354-
type: t.literal('namedterm'),
355-
field: t.string,
356-
value: TermValue.get(c)!,
357-
})
394+
const namedTerm = forPrimitives(
395+
<T extends PrimitiveC>(c: T) =>
396+
t.type({
397+
type: t.literal('namedterm'),
398+
field: t.string,
399+
value: TermValue(c),
400+
}) as t.Type<NamedTerm<t.TypeOf<T>>>
358401
);
402+
export function NamedTerm<T extends PrimitiveC>(
403+
c: T
404+
): t.Type<NamedTerm<t.TypeOf<T>>> {
405+
return namedTerm.get(c)! as t.Type<NamedTerm<t.TypeOf<T>>>;
406+
}
359407
export type SomeNamedTerm = t.Type<NamedTerm<Primitive>>;
360-
export const AnyNamedTerm = t.union(Array.from(NamedTerm).map(x => x[1]) as [
408+
export const AnyNamedTerm = t.union(Array.from(namedTerm).map(x => x[1]) as [
361409
SomeNamedTerm,
362410
SomeNamedTerm,
363411
...SomeNamedTerm[]
@@ -368,14 +416,18 @@ export interface Term<T extends Primitive> {
368416
type: 'term';
369417
value: TermValue<T>;
370418
}
371-
export const Term = forPrimitives(<T extends PrimitiveC>(c: T) =>
372-
t.type({
373-
type: t.literal('term'),
374-
value: TermValue.get(c)!,
375-
})
419+
const term = forPrimitives(
420+
<T extends PrimitiveC>(c: T) =>
421+
t.type({
422+
type: t.literal('term'),
423+
value: TermValue(c),
424+
}) as t.Type<Term<t.TypeOf<T>>>
376425
);
426+
export function Term<T extends PrimitiveC>(c: T): t.Type<Term<t.TypeOf<T>>> {
427+
return term.get(c)! as t.Type<Term<t.TypeOf<T>>>;
428+
}
377429
export type SomeTerm = t.Type<Term<Primitive>>;
378-
export const AnyTerm = t.union(Array.from(Term).map(x => x[1]) as [
430+
export const AnyTerm = t.union(Array.from(term).map(x => x[1]) as [
379431
SomeTerm,
380432
SomeTerm,
381433
...SomeTerm[]
@@ -402,14 +454,14 @@ export const Clause = t.recursion<Clause>(
402454
'Clause',
403455
() =>
404456
t.union([
405-
Term.get(LString)!,
406-
Term.get(LNumber)!,
407-
Term.get(LDate)!,
408-
Term.get(Spatial)!,
409-
NamedTerm.get(LString)!,
410-
NamedTerm.get(LNumber)!,
411-
NamedTerm.get(LDate)!,
412-
NamedTerm.get(Spatial)!,
457+
Term(LString),
458+
Term(LNumber),
459+
Term(LDate),
460+
Term(Spatial),
461+
NamedTerm(LString),
462+
NamedTerm(LNumber),
463+
NamedTerm(LDate),
464+
NamedTerm(Spatial),
413465
ConstantScore,
414466
And,
415467
Or,
@@ -420,10 +472,10 @@ export const Clause = t.recursion<Clause>(
420472
);
421473

422474
export const AnyTermValue = t.union([
423-
TermValue.get(LNumber)!,
424-
TermValue.get(LDate)!,
425-
TermValue.get(LString)!,
426-
TermValue.get(Spatial)!,
475+
TermValue(LString),
476+
TermValue(LNumber),
477+
TermValue(LDate),
478+
TermValue(Spatial),
427479
]);
428480
export type AnyTermValue = t.TypeOf<typeof AnyTermValue>;
429481

test/test-types.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,21 @@ describe('types', () => {
77
describe('codec verification', () => {
88
describe('Range', () => {
99
it('LNumber', () => {
10-
const r = tt.Range.get(tt.LNumber)!;
10+
const r = tt.Range(tt.LNumber);
1111
assert.ok(isRight(r.decode(closedRange(100, 300))));
1212
assert.ok(isLeft(r.decode(closedRange('a', 'bb'))));
1313
assert.ok(isLeft(r.decode(closedRange(undefined, new Date()))));
1414
});
1515

1616
it('LString', () => {
17-
const r = tt.Range.get(tt.LString)!;
17+
const r = tt.Range(tt.LString);
1818
assert.ok(isLeft(r.decode(closedRange(100, 300))));
1919
assert.ok(isRight(r.decode(closedRange('a', 'bb'))));
2020
assert.ok(isLeft(r.decode(closedRange(undefined, new Date()))));
2121
});
2222

2323
it('LDate', () => {
24-
const r = tt.Range.get(tt.LDate)!;
24+
const r = tt.Range(tt.LDate);
2525
assert.ok(isLeft(r.decode(closedRange(100, 300))));
2626
assert.ok(isLeft(r.decode(closedRange('a', 'bb'))));
2727
assert.ok(isRight(r.decode(closedRange(undefined, new Date()))));

yarn.lock

+8-8
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@
9595
resolved "https://registry.yarnpkg.com/@holvonix-open/geojson-io-ts/-/geojson-io-ts-5.0.0.tgz#f3b68d5f566c56ea755291c72bbe979085d001e6"
9696
integrity sha512-Jh6kThOWlL+kPYTwDs0RyPwbw1FBMtcBMYXfDUCtLQY9kKZO4luZsXjqh2ahRRmvuf3RAGfpdLHMwpqKx7cSmw==
9797

98-
"@holvonix-open/release-config-js@^1.0.4":
99-
version "1.0.5"
100-
resolved "https://registry.yarnpkg.com/@holvonix-open/release-config-js/-/release-config-js-1.0.5.tgz#d64d2fe39124ef2042faad7cd18b14a8c3c4c97b"
101-
integrity sha512-WhbTtJz64Q00G/xRzeZ/fuAF9toTIBvd8bGen8Y2mGsOv7pRhaQoNm0xftSxyPEwsb5IMhHS/VNawbjPw5giaQ==
98+
"@holvonix-open/release-config-js@^1.0.6":
99+
version "1.0.6"
100+
resolved "https://registry.yarnpkg.com/@holvonix-open/release-config-js/-/release-config-js-1.0.6.tgz#471a0558996c3938464492d63e5deeb06a116d5e"
101+
integrity sha512-Ld7Pacs9XUF7y6DMtQv3V5oIWukoFR2Tq68Rbnbliu3YX4a6k9li/i4Br5JLznbmBh3S3Gp8K9wJ3nSg5X2+9g==
102102
dependencies:
103103
"@semantic-release/changelog" "^3.0.4"
104104
"@semantic-release/git" "^7.0.16"
@@ -5612,10 +5612,10 @@ windows-release@^3.1.0:
56125612
dependencies:
56135613
execa "^1.0.0"
56145614

5615-
wkt-io-ts@^1.0.0:
5616-
version "1.0.0"
5617-
resolved "https://registry.yarnpkg.com/wkt-io-ts/-/wkt-io-ts-1.0.0.tgz#a68d0a3c534a66377ae851617cdd41f643e15ae5"
5618-
integrity sha512-DhFv1zt6lwojE3ZSdxP7KOt4HXVmLplsLbyiw48ldFsaYQjzU2vzPyeDQSBn3YJYS82EqZYlLknKXjHYcetzHg==
5615+
wkt-io-ts@^1.0.1:
5616+
version "1.0.1"
5617+
resolved "https://registry.yarnpkg.com/wkt-io-ts/-/wkt-io-ts-1.0.1.tgz#b5b9beb1a17312c5e1a4f125fbfda2930c73cd31"
5618+
integrity sha512-H7FJ9DAbrA1rfcrVfjr4WqajwO/Iog++srVN0FebIXrOs7/bcMIqVkWg3XgQMHzZOZGc8F3mUGyAO0zwg9XC/Q==
56195619
dependencies:
56205620
"@holvonix-open/geojson-io-ts" "^5.0.0"
56215621
fp-ts "^2.0.5"

0 commit comments

Comments
 (0)