如何在io-ts库中使用@types/fabric预定义接口实现运行时类型检查?
The core issue you're hitting is that TypeScript interfaces are compile-only constructs—they don't exist in runtime, so io-ts can't use them directly for validation. The example from io-ts issue #209 references generating io-ts codecs from TypeScript types (not raw interfaces), which requires mapping fabric's pre-defined types to runtime-validatable io-ts structures. Here's how to make this work:
Solution: Map Fabric Interfaces to io-ts Codecs
Since you can't pass interfaces directly to io-ts, you need to create io-ts Codec instances that mirror the structure of fabric's ICircleOptions, IRectOptions, and other shapes. Here's a step-by-step implementation:
1. Define Base Fabric Object Properties
First, create a partial codec for properties shared across most fabric objects (since ICircleOptions and IRectOptions inherit from base fabric interfaces):
import * as t from 'io-ts'; import type { ICircleOptions, IRectOptions } from 'fabric'; // Partial codec for shared fabric object options (adjust based on your validation needs) const BaseFabricObject = t.partial({ left: t.number, top: t.number, fill: t.string, stroke: t.string, strokeWidth: t.number, opacity: t.number, selectable: t.boolean, // Add any other properties you need to validate });
2. Create Codecs for Specific Fabric Shapes
Combine the base properties with shape-specific fields, and add a type literal to help io-ts distinguish between shapes during validation:
// Codec mirroring ICircleOptions const CircleOptions = t.intersection([ BaseFabricObject, t.type({ type: t.literal('circle'), // Explicit type marker for union discrimination radius: t.number, }), ]); // Codec mirroring IRectOptions const RectOptions = t.intersection([ BaseFabricObject, t.type({ type: t.literal('rect'), width: t.number, height: t.number, }), ]);
3. Build Your Final Response Codec
Now you can use these shape codecs in your Response definition:
const Response = t.type({ id: t.number, type: t.string, objects: t.array(t.union([CircleOptions, RectOptions])), }); // Optional: Get the TypeScript type from the codec for compile-time safety type ResponseType = t.TypeOf<typeof Response>;
4. Validate Data at Runtime
Use io-ts's decode method to validate incoming data:
const sampleData = { id: 1, type: 'canvas', objects: [ { type: 'circle', radius: 50, fill: 'red', left: 100 }, { type: 'rect', width: 200, height: 100, stroke: 'blue' } ] }; const result = Response.decode(sampleData); if (result._tag === 'Right') { // Valid data: result.right is typed as ResponseType console.log('Valid data:', result.right); } else { // Invalid data: result.left contains detailed error messages console.error('Validation failed:', result.left); }
Alternative: Auto-Generate Codecs (Advanced)
If you don't want to manually map every fabric interface, use these tools to auto-generate codecs:
- io-ts-codegen: Generate io-ts code from TypeScript type definitions (extract the fabric types you need into a separate file first).
- ts-json-schema-generator: Convert TypeScript interfaces to JSON Schema, then use
io-ts-from-schemato generate codecs from the schema.
Note that auto-generation may require tweaks, as fabric's type definitions are complex and include optional/union types that need special handling.
内容的提问来源于stack exchange,提问作者Abhishek T.

