Skip to Content
Type Inference

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 }
Last updated on