Type inference
Input and output inference is performed by universal types that can be applied both to RPC modules and to controller methods.
Client-side inference:
import type { VovkBody, VovkQuery, VovkParams, VovkOutput, VovkIteration, VovkReturnType, VovkYieldType } from 'vovk';
import { UserRPC, StreamRPC } from 'vovk-client';
// infer input
type Body = VovkBody<typeof UserRPC.updateUser>;
type Query = VovkQuery<typeof UserRPC.updateUser>;
type Params = VovkParams<typeof UserRPC.updateUser>;
// infer output
type Output = VovkOutput<typeof UserRPC.updateUser>;
type Iteration = VovkIteration<typeof StreamRPC.streamTokens>;
// see below
type Return = VovkReturnType<typeof UserRPC.updateUser>;
type Yield = VovkYieldType<typeof StreamRPC.streamTokens>;
Server-side inference:
import type { VovkBody, VovkQuery, VovkParams, VovkOutput, VovkIteration, VovkReturnType, VovkYieldType } from 'vovk';
import type UserController from './UserController';
import type StreamController from './StreamController';
// infer input
type Body = VovkBody<typeof UserController.updateUser>;
type Query = VovkQuery<typeof UserController.updateUser>;
type Params = VovkParams<typeof UserController.updateUser>;
// infer output
type Output = VovkOutput<typeof UserController.updateUser>;
type Iteration = VovkIteration<typeof StreamController.streamTokens>;
// see below
type Return = VovkReturnType<typeof UserController.updateUser>;
type Yield = VovkYieldType<typeof StreamController.streamTokens>;
Input inference
The source body, query and params types are defined using VovkRequest<TBody, TQuery, ?TParams>
that defines the type of req
argument of the controller method. In other words, raw method definition and validated method definition define the input types for body
, query
and params
.
Raw method definition with param as generic argument:
import type { VovkRequest } from 'vovk';
export default class UserController {
@get()
static async updateUser(req: VovkRequest<{ email: string }, { id: string }, { param: string }>) {
// ...
}
}
Raw method definition with param as separate argument:
import type { VovkRequest } from 'vovk';
export default class UserController {
@get()
static async updateUser(req: VovkRequest<{ email: string }, { id: string }>, params: { param: string }) {
// ...
}
}
Validated method definition:
import { withZod } from 'vovk-zod';
import { z } from 'zod';
export default class UserController {
@get()
static updateUser = withZod({
query: z.object({ id: z.string() }),
params: z.object({ param: z.string() }),
body: z.object({ email: z.string().email() }),
async handle(req, params) {
// ...
},
});
}
All three cases will geneerate the same RPC method:
import { UserRPC } from 'vovk-client';
await UserRPC.updateUser({
params: { param: 'value' },
query: { id: 'value' },
body: { email: 'value' },
});
And all three cases will allow to infer the input types the same way:
import type { VovkBody, VovkQuery, VovkParams } from 'vovk';
import { UserRPC } from 'vovk-client';
type Body = VovkBody<typeof UserRPC.updateUser>; // { email: string }
type Query = VovkQuery<typeof UserRPC.updateUser>; // { id: string }
type Params = VovkParams<typeof UserRPC.updateUser>; // { param: string }
import type { VovkBody, VovkQuery, VovkParams } from 'vovk';
import type UserController from './UserController';
type Body = VovkBody<typeof UserController.updateUser>; // { email: string }
type Query = VovkQuery<typeof UserController.updateUser>; // { id: string }
type Params = VovkParams<typeof UserController.updateUser>; // { param: string }
Output/iteration inference
The source output and iteration types are defined with validation libraries only.
For output:
import { withZod } from 'vovk-zod';
import { z } from 'zod';
export default class UserController {
@get()
static updateUser = withZod({
output: z.object({ success: z.boolean() }),
async handle(req, params) {
return { success: true };
},
});
}
For iteration (streaming responses):
import { withZod } from 'vovk-zod';
import { z } from 'zod';
export default class StreamController {
@get()
static streamItems = withZod({
iteration: z.object({ item: z.boolean() }),
async *handle(req, params) {
yield { item: true };
yield { item: true };
},
});
}
import type { VovkOutput, VovkIteration } from 'vovk';
import { UserRPC } from 'vovk-client';
type Output = VovkOutput<typeof UserRPC.updateUser>; // { success: boolean }
type Iteration = VovkIteration<typeof StreamRPC.streamItems>; // { item: boolean }
import type { VovkOutput, VovkIteration } from 'vovk';
import type UserController from './UserController';
import type StreamController from './StreamController';
type Output = VovkOutput<typeof UserController.updateUser>; // { success: boolean }
type Iteration = VovkIteration<typeof StreamController.streamItems>; // { item: boolean }
Return/yield inference
The VovkReturnType<T>
and VovkYieldType<T>
types allow to infer the actual return type or the yield type of a method when it’s input isn’t validated. The types cannot be used for self-references at services as they cause “implicit any” TS errors.
export default class UserController {
@get()
static updateUser = () => {
return { success: true };
}
}
export default class StreamController {
@get()
static async *streamItems() {
yield { item: true };
yield { item: true };
}
}
import type { VovkReturnType, VovkYieldType } from 'vovk';
import { UserRPC, StreamRPC } from 'vovk-client';
type Return = VovkReturnType<typeof UserRPC.updateUser>; // { success: boolean }
type Yield = VovkYieldType<typeof StreamRPC.streamItems>; // { item: boolean }
import type { VovkReturnType, VovkYieldType } from 'vovk';
import type UserController from './UserController';
import type StreamController from './StreamController';
type Return = VovkReturnType<typeof UserController.updateUser>; // { success: boolean }
type Yield = VovkYieldType<typeof StreamController.streamItems>; // { item: boolean }