Skip to content

Core API

defineCommand

Type-safe command definitions with full TypeScript inference

defineCommand#

defineCommand is a helper that gives you fully inferred TypeScript types in your handler based on the param schema you declare โ€” with zero runtime overhead (identity function).

TypeScript
import { createSurf, defineCommand } from '@surfjs/core'
ย 
// params are automatically typed from the schema
const search = defineCommand({
description: 'Search products',
params: {
query: { type: 'string', required: true, description: 'Search term' },
limit: { type: 'number', description: 'Max results' },
},
run(params) {
// params.query โ†’ string (required โ€” never undefined)
// params.limit โ†’ number | undefined (optional)
return db.products.search(params.query, { limit: params.limit })
},
})
ย 
const surf = await createSurf({
name: 'My Store',
commands: { search },
})

๐Ÿ’ก Tip: Without defineCommand, handlers receive Record<string, unknown>. With it, required params are string / number / boolean and optional params include | undefined โ€” all inferred automatically.

Use defineCommand when you want type-safe handlers without writing manual interfaces. It's especially useful for large APIs with many commands.


InferParam and InferParams

Lower-level utility types for extracting the TypeScript type of a single param or a full params object from a ParamSchema. Useful when building abstractions on top of Surf.

TypeScript
import type { InferParam, InferParams } from '@surfjs/core'
import type { ParamSchema } from '@surfjs/core'
ย 
// Infer the type of a single param definition
type QueryType = InferParam<{ type: 'string'; required: true }>
// => string
ย 
type LimitType = InferParam<{ type: 'number' }>
// => number (InferParam maps to the base type โ€” optionality is handled by InferParams)
ย 
// Infer the full params object from a ParamSchema map
const myParams = {
query: { type: 'string', required: true },
limit: { type: 'number' },
} satisfies Record<string, ParamSchema>
ย 
type MyParams = InferParams<typeof myParams>
// => { query: string; limit?: number }
// Required keys are non-optional; all others get `?` (optional)

Key distinction: InferParam maps a single schema to its base TypeScript type (string, number, boolean, etc.). It does not add | undefined โ€” that's handled by InferParams, which splits required vs optional keys and applies ? to optional ones. Use InferParam for individual type extraction and InferParams for full param objects.

defineCommand uses these types internally. Import them directly when you need to type-check param schemas in utility functions, generic wrappers, or custom command builders.