Skip to content

Commit 06dfbe8

Browse files
authored
Merge pull request #39 from jsonjoy-com/impr-fn
Impr fn
2 parents d14712b + ae4fd05 commit 06dfbe8

File tree

7 files changed

+58
-35
lines changed

7 files changed

+58
-35
lines changed

src/schema/schema.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -412,19 +412,22 @@ export interface OrSchema<T extends TType[] = TType[]> extends TType {
412412

413413
export type FunctionValue<Req, Res, Ctx = unknown> = (req: Req, ctx?: Ctx) => Res | Promise<Res>;
414414

415-
export interface FunctionSchema<Req extends TType = TType, Res extends TType = TType> extends TType {
415+
export interface FunctionSchema<Req extends TType = TType, Res extends TType = TType, Ctx = unknown> extends TType {
416416
kind: 'fn';
417417
req: Req;
418418
res: Res;
419+
__ctx_brand?: Ctx;
419420
}
420421

421422
export type FunctionStreamingValue<Req, Res, Ctx = unknown> = (req: Observable<Req>, ctx?: Ctx) => Observable<Res>;
422423

423-
export interface FunctionStreamingSchema<Req extends TType = TType, Res extends TType = TType> extends TType {
424+
export interface FunctionStreamingSchema<Req extends TType = TType, Res extends TType = TType, Ctx = unknown>
425+
extends TType {
424426
/** @todo Rename to `fn`. Make it a property on the schema instead. */
425427
kind: 'fn$';
426428
req: Req;
427429
res: Res;
430+
__ctx_brand?: Ctx;
428431
}
429432

430433
export interface TypeSystemSchema {
@@ -480,10 +483,10 @@ export type TypeOfValue<T> = T extends BooleanSchema
480483
? Record<string, TypeOf<U>>
481484
: T extends BinarySchema
482485
? Uint8Array
483-
: T extends FunctionSchema<infer Req, infer Res>
484-
? (req: TypeOf<Req>, ctx?: unknown) => Promise<TypeOf<Res>>
485-
: T extends FunctionStreamingSchema<infer Req, infer Res>
486-
? (req$: Observable<TypeOf<Req>>, ctx?: unknown) => Observable<TypeOf<Res>>
486+
: T extends FunctionSchema<infer Req, infer Res, infer Ctx>
487+
? (req: TypeOf<Req>, ctx: Ctx) => UndefToVoid<TypeOf<Res>> | Promise<UndefToVoid<TypeOf<Res>>>
488+
: T extends FunctionStreamingSchema<infer Req, infer Res, infer Ctx>
489+
? (req$: Observable<TypeOf<Req>>, ctx: Ctx) => Observable<UndefToVoid<TypeOf<Res>>>
487490
: never;
488491

489492
export type TypeOfMap<M extends Record<string, Schema>> = {
@@ -510,6 +513,8 @@ type TypeOfFieldMap<T> = {[K in keyof T]: TypeOf<FieldValue<T[K]>>};
510513

511514
type FieldValue<F> = F extends ObjectFieldSchema<any, infer V> ? V : never;
512515

516+
type UndefToVoid<T> = T extends undefined ? void : T;
517+
513518
export type OptionalProps<T extends object> = Exclude<
514519
{
515520
[K in keyof T]: T extends Record<K, T[K]> ? never : K;

src/type/TypeBuilder.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,22 +246,22 @@ export class TypeBuilder {
246246
return type;
247247
}
248248

249-
public Function<Req extends Type, Res extends Type>(
249+
public Function<Req extends Type, Res extends Type, Ctx = unknown>(
250250
req: Req,
251251
res: Res,
252252
options?: schema.Optional<schema.FunctionSchema>,
253253
) {
254-
const fn = new classes.FnType<Req, Res>(req, res, options);
254+
const fn = new classes.FnType<Req, Res, Ctx>(req, res, options);
255255
fn.system = this.system;
256256
return fn;
257257
}
258258

259-
public Function$<Req extends Type, Res extends Type>(
259+
public Function$<Req extends Type, Res extends Type, Ctx = unknown>(
260260
req: Req,
261261
res: Res,
262262
options?: schema.Optional<schema.FunctionStreamingSchema>,
263263
) {
264-
const fn = new classes.FunctionStreamingType<Req, Res>(req, res, options);
264+
const fn = new classes.FnRxType<Req, Res, Ctx>(req, res, options);
265265
fn.system = this.system;
266266
return fn;
267267
}

src/type/__tests__/TypeBuilder.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {NumType, ObjectFieldType, ObjType, StrType} from '../classes';
22
import {type SchemaOf, t} from '..';
33
import type {TypeOf} from '../../schema';
4+
import {fn} from '../../random';
45

56
test('number', () => {
67
const type = t.Number({
@@ -114,11 +115,25 @@ test('can build type using lowercase shortcuts', () => {
114115
offsets: [1, 2, 3],
115116
enum: 'three',
116117
optional: undefined,
117-
} as ObjType2;
118+
} satisfies ObjType2;
118119
MyObject.validate(obj);
119120
MyObject2.validate(obj);
120121
});
121122

123+
test('can specify function with context', () => {
124+
const MyObject = t.object({
125+
fn: t.fn.inp(t.str).out(t.undef).ctx<{ip: string}>(),
126+
});
127+
// console.log(MyObject + '');
128+
const MyObject2 = t.obj.prop('fn', t.Function(t.str, t.undef).ctx<{ip: string}>());
129+
expect(MyObject.getSchema()).toEqual(MyObject2.getSchema());
130+
type ObjType = t.infer<typeof MyObject>;
131+
type ObjType2 = t.infer<typeof MyObject2>;
132+
const obj: ObjType = {
133+
fn: async (req: string, ctx: {ip: string}): Promise<void> => {},
134+
} satisfies ObjType2;
135+
});
136+
122137
describe('import()', () => {
123138
test('can import a number schema', () => {
124139
const type = t.import({

src/type/classes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {ObjType, ObjectFieldType, ObjectOptionalFieldType} from './classes/ObjTy
1111
import {MapType} from './classes/MapType';
1212
import {RefType} from './classes/RefType';
1313
import {OrType} from './classes/OrType';
14-
import {FnType, FunctionStreamingType} from './classes/FnType';
14+
import {FnType, FnRxType} from './classes/FnType';
1515

1616
export {
1717
AbsType,
@@ -30,5 +30,5 @@ export {
3030
RefType,
3131
OrType,
3232
FnType,
33-
FunctionStreamingType,
33+
FnRxType,
3434
};

src/type/classes/FnType.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ import {printTree} from 'tree-dump/lib/printTree';
22
import * as schema from '../../schema';
33
import {AbsType} from './AbsType';
44
import type {SchemaOf, Type} from '../types';
5-
import type * as ts from '../../typescript/types';
65
import type {ResolveType} from '../../system';
76
import type {Observable} from 'rxjs';
87

98
const fnNotImplemented: schema.FunctionValue<any, any> = async () => {
109
throw new Error('NOT_IMPLEMENTED');
1110
};
1211

13-
const toStringTree = (tab: string = '', type: FnType<Type, Type> | FunctionStreamingType<Type, Type>) => {
12+
const toStringTree = (tab: string = '', type: FnType<Type, Type, any> | FnRxType<Type, Type, any>) => {
1413
return printTree(tab, [
1514
(tab) => 'req: ' + type.req.toString(tab + ' '),
1615
(tab) => 'res: ' + type.res.toString(tab + ' '),
@@ -20,12 +19,12 @@ const toStringTree = (tab: string = '', type: FnType<Type, Type> | FunctionStrea
2019
type FunctionImpl<Req extends Type, Res extends Type, Ctx = unknown> = (
2120
req: ResolveType<Req>,
2221
ctx: Ctx,
23-
) => Promise<ResolveType<Res>>;
22+
) => ResolveType<Res> | Promise<ResolveType<Res>>;
2423

25-
export class FnType<Req extends Type, Res extends Type> extends AbsType<
26-
schema.FunctionSchema<SchemaOf<Req>, SchemaOf<Res>>
24+
export class FnType<Req extends Type, Res extends Type, Ctx = unknown> extends AbsType<
25+
schema.FunctionSchema<SchemaOf<Req>, SchemaOf<Res>, Ctx>
2726
> {
28-
protected schema: schema.FunctionSchema<SchemaOf<Req>, SchemaOf<Res>>;
27+
protected schema: schema.FunctionSchema<SchemaOf<Req>, SchemaOf<Res>, Ctx>;
2928

3029
public fn: schema.FunctionValue<schema.TypeOf<SchemaOf<Req>>, schema.TypeOf<SchemaOf<Res>>> = fnNotImplemented;
3130

@@ -59,17 +58,21 @@ export class FnType<Req extends Type, Res extends Type> extends AbsType<
5958
return this.response(res);
6059
}
6160

62-
public getSchema(): schema.FunctionSchema<SchemaOf<Req>, SchemaOf<Res>> {
61+
public ctx<T>(): FnType<Req, Res, T> {
62+
return this as any;
63+
}
64+
65+
public getSchema(): schema.FunctionSchema<SchemaOf<Req>, SchemaOf<Res>, Ctx> {
6366
return {
6467
...this.schema,
6568
req: this.req.getSchema() as SchemaOf<Req>,
6669
res: this.res.getSchema() as SchemaOf<Res>,
6770
};
6871
}
6972

70-
public singleton?: FunctionImpl<Req, Res, any> = undefined;
73+
public singleton?: FunctionImpl<Req, Res, Ctx> = undefined;
7174

72-
public implement<Ctx = unknown>(singleton: FunctionImpl<Req, Res, Ctx>): this {
75+
public implement(singleton: FunctionImpl<Req, Res, Ctx>): this {
7376
this.singleton = singleton;
7477
return this;
7578
}
@@ -84,11 +87,11 @@ type FunctionStreamingImpl<Req extends Type, Res extends Type, Ctx = unknown> =
8487
ctx: Ctx,
8588
) => Observable<ResolveType<Res>>;
8689

87-
export class FunctionStreamingType<Req extends Type, Res extends Type> extends AbsType<
88-
schema.FunctionStreamingSchema<SchemaOf<Req>, SchemaOf<Res>>
90+
export class FnRxType<Req extends Type, Res extends Type, Ctx = unknown> extends AbsType<
91+
schema.FunctionStreamingSchema<SchemaOf<Req>, SchemaOf<Res>, Ctx>
8992
> {
9093
public readonly isStreaming = true;
91-
protected schema: schema.FunctionStreamingSchema<SchemaOf<Req>, SchemaOf<Res>>;
94+
protected schema: schema.FunctionStreamingSchema<SchemaOf<Req>, SchemaOf<Res>, Ctx>;
9295

9396
constructor(
9497
public readonly req: Req,
@@ -120,17 +123,17 @@ export class FunctionStreamingType<Req extends Type, Res extends Type> extends A
120123
return this.response(res);
121124
}
122125

123-
public getSchema(): schema.FunctionStreamingSchema<SchemaOf<Req>, SchemaOf<Res>> {
126+
public getSchema(): schema.FunctionStreamingSchema<SchemaOf<Req>, SchemaOf<Res>, Ctx> {
124127
return {
125128
...this.schema,
126129
req: this.req.getSchema() as SchemaOf<Req>,
127130
res: this.res.getSchema() as SchemaOf<Res>,
128131
};
129132
}
130133

131-
public singleton?: FunctionStreamingImpl<Req, Res, any> = undefined;
134+
public singleton?: FunctionStreamingImpl<Req, Res, Ctx> = undefined;
132135

133-
public implement<Ctx = unknown>(singleton: FunctionStreamingImpl<Req, Res, Ctx>): this {
136+
public implement(singleton: FunctionStreamingImpl<Req, Res, Ctx>): this {
134137
this.singleton = singleton;
135138
return this;
136139
}

src/type/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
export * from './types';
22
export * from './classes';
33

4-
import type {TypeOf} from '../schema';
54
import {TypeBuilder} from './TypeBuilder';
5+
import type {TypeOf} from '../schema';
66
import type {SchemaOf, Type} from './types';
77

88
export const t = new TypeBuilder();

src/type/types.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export type Type =
2020
| classes.MapType<any>
2121
| classes.RefType<any>
2222
| classes.OrType<any>
23-
| classes.FnType<any, any>
24-
| classes.FunctionStreamingType<any, any>;
23+
| classes.FnType<any, any, any>
24+
| classes.FnRxType<any, any, any>;
2525

2626
export type SchemaOf<T extends Type | Type[]> = T extends BaseType<infer U> ? U : never;
2727
export type SchemaOfMap<M extends Record<string, Type>> = {
@@ -41,13 +41,13 @@ export type SchemaOfObjectFields<F> = {
4141
export type TypeMap = {[name: string]: schema.Schema};
4242

4343
export type FilterFunctions<T> = {
44-
[K in keyof T as T[K] extends classes.FnType<any, any>
44+
[K in keyof T as T[K] extends classes.FnType<any, any, any>
4545
? K
46-
: T[K] extends classes.FunctionStreamingType<any, any>
46+
: T[K] extends classes.FnRxType<any, any, any>
4747
? K
48-
: never]: T[K] extends classes.FnType<any, any>
48+
: never]: T[K] extends classes.FnType<any, any, any>
4949
? T[K]
50-
: T[K] extends classes.FunctionStreamingType<any, any>
50+
: T[K] extends classes.FnRxType<any, any, any>
5151
? T[K]
5252
: never;
5353
};

0 commit comments

Comments
 (0)