-
-
Notifications
You must be signed in to change notification settings - Fork 197
Description
Background
Type-safe RPC frameworks like SvelteKit's remote functions and tRPC rely on StandardSchema for parameter validation while maintaining end-to-end type safety. StandardSchema is an interface that separates Input and Output types, allowing type inference through InferInput and InferOutput utility types.
Typia added StandardSchema support to createValidate and createValidateEquals functions starting from v9.2.0. However, the current implementation doesn't properly propagate type information, causing DX issues in type-safe RPC frameworks.
Problem
The current type signature of typia.createValidate<T>() is:
function createValidate<T>(): ((input: unknown) => IValidation<T>) & StandardSchemaV1<unknown, T>Issues with this signature:
-
Input type is hardcoded as
unknown: InStandardSchemaV1<unknown, T>, the first generic parameter (Input) is hardcoded asunknown. -
Type inference failure in RPC frameworks: Type-safe RPC frameworks use
StandardSchemaV1.InferInput<Schema>to infer parameter types for client-side calls. However, with typia, this type is always inferred asunknown, breaking auto-completion and type checking. -
Practical example:
// data.remote.ts (SvelteKit)
import typia from 'typia';
import { query } from '$app/server';
interface Post {
slug: string;
title: string;
content: string;
}
const postSchema = typia.createValidate<Post>();
export const getPost = query(postSchema, async (params) => {
params.slug // ✅ Server-side type inference works correctly (Post type)
// ...
});Server-side type inference works correctly. However, the problem occurs on the client-side:
// +page.svelte
const post = await getPost(/* ❌ Parameter type is inferred as unknown */);The inferred type shows:
RemoteQueryFunction<unknown, { content: string; slug: string; title: string; }>Since the first generic of RemoteQueryFunction is unknown:
- Auto-completion doesn't work when calling the function on the client
- Type checking doesn't work (no error even when passing wrong arguments)
- Developers lose the benefits of end-to-end type safety
Root cause: Because the Input type in StandardSchemaV1<unknown, Post> is unknown, the framework's InferInput utility returns unknown.
Expected Behavior
StandardSchema separates Input and Output types to allow schema libraries that support data transformation or type coercion to clearly distinguish between input and output types. The interface is defined as StandardSchemaV1<Input = unknown, Output = Input>, explicitly stating that Input and Output can be different.
The reason for separating Input and Output types:
- Transformation: When using transform APIs in libraries like Zod, input and output types can differ:
// Zod example
const mySchema = z.string().transform((val) => val.length);
// Input: string
// Output: number-
Coercion: When using type coercion, the type at input time can differ from the type after validation (e.g., converting string "123" to number 123).
-
Data enrichment: Adding default values, computed fields, etc.
In typia's case, since createValidate only performs validation without any data transformation, Input and Output should be the same type T:
function createValidate<T>(): ((input: unknown) => IValidation<T>) & StandardSchemaV1<T, T>Proposed Solution
Change the Input type parameter to T:
function createValidate<T>(): ((input: unknown) => IValidation<T>) & StandardSchemaV1<T, T>This change will:
- Enable perfect type inference in type-safe RPC frameworks
- Restore end-to-end type safety that these frameworks provide
- Improve type safety and auto-completion support for developers
- Maintain consistency with other StandardSchema implementations (Zod, Valibot, etc.)
- Align with typia's actual behavior (no transformation)
PR 열까유?