Type Inference
These types infer the input and output of both RPC modules and controller procedures.
Client-side inference:
import type { VovkBody, VovkQuery, VovkParams, VovkInput, VovkOutput, VovkIteration, VovkReturnType, VovkYieldType } from 'vovk';
import { UserRPC, StreamRPC } from '@/client';
// infer input
type Body = VovkBody<typeof UserRPC.updateUser>;
type Query = VovkQuery<typeof UserRPC.updateUser>;
type Params = VovkParams<typeof UserRPC.updateUser>;
type Input = VovkInput<typeof UserRPC.updateUser>; // { params, query, body }
// 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, VovkInput, VovkOutput, VovkIteration, VovkReturnType, VovkYieldType } from 'vovk';
import type UserController from './user-controller';
import type StreamController from './stream-controller';
// infer input
type Body = VovkBody<typeof UserController.updateUser>;
type Query = VovkQuery<typeof UserController.updateUser>;
type Params = VovkParams<typeof UserController.updateUser>;
type Input = VovkInput<typeof UserController.updateUser>; // { params, query, body }
// 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 types of body, query and params come from VovkRequest<TBody, TQuery, ?TParams>, the type of the procedure’s req argument. So both raw and validated methods set the input types.
A raw method with params as a type argument:
import { put, type VovkRequest } from 'vovk';
export default class UserController {
@put('{param}')
static async updateUser(req: VovkRequest<{ email: string }, { id: string }, { param: string }>) {
// ...
}
}A raw method with params as a separate argument:
import { put, type VovkRequest } from 'vovk';
export default class UserController {
@put('{param}')
static async updateUser(req: VovkRequest<{ email: string }, { id: string }>, params: { param: string }) {
// ...
}
}Validated procedures infer the input types from their schemas:
import { procedure, put } from 'vovk';
import { z } from 'zod';
export default class UserController {
@put('{param}')
static updateUser = procedure({
query: z.object({ id: z.string() }),
params: z.object({ param: z.string() }),
body: z.object({ email: z.string().email() }),
}).handle((req, params) => {
// ...
});
}All three give the same RPC method:
import { UserRPC } from '@/client';
await UserRPC.updateUser({
params: { param: 'value' },
query: { id: 'value' },
body: { email: 'value' },
});And the same inferred input types:
import type { VovkBody, VovkQuery, VovkParams } from 'vovk';
import { UserRPC } from '@/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 './user-controller';
type Body = VovkBody<typeof UserController.updateUser>; // { email: string }
type Query = VovkQuery<typeof UserController.updateUser>; // { id: string }
type Params = VovkParams<typeof UserController.updateUser>; // { param: string }For a procedure, the RPC method and fn take what the schemas accept (their input types), and the handler gets what the schemas return (their output types). The two differ when a schema has a default, a coercion or a transform. With body: z.object({ tags: z.string().transform((s) => s.split(',')) }), VovkBody of the RPC method is { tags: string }, and VovkBody of the controller method is { tags: string[] }.
Combined Input Type
VovkInput<T> puts all three input types (params, query, body) in one object.
import type { VovkInput } from 'vovk';
import type UserController from './user-controller';
type Input = VovkInput<typeof UserController.updateUser>;
// { params: { param: string }; query: { id: string }; body: { email: string } }It works the same with RPC modules:
import type { VovkInput } from 'vovk';
import { UserRPC } from '@/client';
type Input = VovkInput<typeof UserRPC.updateUser>;
// { params: { param: string }; query: { id: string }; body: { email: string } }Output/Iteration Inference
Only the procedure function sets output and iteration types.
For output:
import { procedure, get } from 'vovk';
import { z } from 'zod';
export default class UserController {
@get()
static updateUser = procedure({
output: z.object({ success: z.boolean() }),
}).handle(async (req, params) => {
return { success: true };
});
}For JSON Lines responses:
import { procedure, get } from 'vovk';
import { z } from 'zod';
export default class StreamController {
@get()
static streamItems = procedure({
iteration: z.object({ item: z.boolean() }),
}).handle(function *(req, params) {
yield { item: true };
yield { item: true };
});
}import type { VovkOutput, VovkIteration } from 'vovk';
import { UserRPC, StreamRPC } from '@/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 './user-controller';
import type StreamController from './stream-controller';
type Output = VovkOutput<typeof UserController.updateUser>; // { success: boolean }
type Iteration = VovkIteration<typeof StreamController.streamItems>; // { item: boolean }Return/Yield Inference
VovkReturnType<T> and VovkYieldType<T> infer what a method actually returns or yields, for methods without validation. They can’t be used for self-references in services: they cause “implicit any” TypeScript 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 '@/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 './user-controller';
import type StreamController from './stream-controller';
type Return = VovkReturnType<typeof UserController.updateUser>; // { success: boolean }
type Yield = VovkYieldType<typeof StreamController.streamItems>; // { item: boolean }