๐Ÿ› Bug Fixes

Recent fixes and improvements

โœ… Critical Bugs Fixed

We've resolved two major issues affecting array-of-objects validation:

  • TypeScript Type Inference: Arrays now correctly infer as Array<T> instead of tuple types
  • Runtime Validation: Array elements are now properly validated against their schema

Array-of-Objects Type Inference

FIXED

โŒ Problem

When defining array-of-objects using the syntax videos: [{ ... }], TypeScript was incorrectly inferring it as a tuple type requiring exactly one element, instead of Array<T>.

TypeScript Error:

// Before fix - TypeScript error
const schema = Interface({
  videos: [{
    id: "string",
    title: "string"
  }]
});

const data = {
  videos: [] // โŒ Error: Property '0' is missing in type '[]'
};

๐Ÿ” Root Cause

In src/core/schema/mode/interfaces/typescript/TypeInference.ts, the InferFieldType type didn't check for readonly [T] syntax before checking Array<T>. TypeScript treats [{...}] as a tuple type by default.

// Before - Missing tuple check
export type InferFieldType<T> = 
  T extends string
    ? HandleOptional<MapFieldType<T>, IsOptional<T>>
    : T extends Array<infer U>  // โŒ Doesn't match tuple types
      ? Array<InferSchemaType<U>>
      : // ... other types

โœ… Solution

Added check for T extends readonly [infer U] before the Array check. This ensures tuple syntax is properly detected and converted to array type.

// After - Tuple check added
export type InferFieldType<T> = 
  T extends string
    ? HandleOptional<MapFieldType<T>, IsOptional<T>>
    : T extends readonly [infer U]  // โœ… Check tuple first
      ? Array<InferSchemaType<U>>
      : T extends Array<infer U>
        ? Array<InferSchemaType<U>>
        : // ... other types

๐ŸŽ‰ Result

// After fix - Works correctly
const schema = Interface({
  videos: [{
    id: "string",
    title: "string"
  }]
});

type InferredType = InferType<typeof schema>;
// โœ… { videos: Array<{ id: string; title: string }> }

const data1 = { videos: [] }; // โœ… Valid
const data2 = { videos: [{ id: "1", title: "Video" }] }; // โœ… Valid

Array-of-Objects Runtime Validation

FIXED

โŒ Problem

Array-of-objects validation was completely broken. Invalid data was passing through without any validation:

โŒ Missing Fields

Required fields were not validated

โŒ Wrong Types

Type mismatches were ignored

โŒ Not Even Array

Objects passed as arrays

// Before fix - All these incorrectly passed โŒ
const schema = Interface({
  videos: [{
    id: "string",
    title: "string",
    duration: "number"
  }]
});

// Missing required fields - should FAIL but PASSED
schema.parse({ videos: [{ id: "1" }] }); // โŒ

// Wrong types - should FAIL but PASSED
schema.parse({ videos: [{ id: 123, title: "Test", duration: "wrong" }] }); // โŒ

// Not even an array - should FAIL but PASSED
schema.parse({ videos: { id: "1", title: "Test" } }); // โŒ

๐Ÿ” Root Cause

The precompiled validator was being used for schemas with array-of-objects, but the precompiler doesn't handle them. It was calling String(fieldType) on the array [{ ... }], converting it to "[object Object]", and creating a broken validator that did nothing.

// In SchemaPrecompiler.ts - The bug
private static compileField(fieldName: string, fieldType: any) {
  if (typeof fieldType === "object" && !Array.isArray(fieldType)) {
    // Handle nested objects
  }
  
  // โŒ BUG: Arrays fall through to here
  const fieldTypeStr = String(fieldType); // "[object Object]"
  const validator = FieldPrecompilers.parseAndCompile(fieldTypeStr);
  // Creates broken validator!
}

โœ… Solution

Added check in InterfaceSchema.ts to detect array-of-objects fields and skip precompilation, forcing the use of standard validation which correctly handles arrays.

// In InterfaceSchema.ts - The fix
private createPrecompiledValidator(): void {
  const hasConditionalFields = this.compiledFields.some(
    (field) => field.isConditional
  );
  
  // โœ… NEW: Check for array-of-objects fields
  const hasArrayOfObjects = this.compiledFields.some(
    (field) => Array.isArray(field.originalType)
  );
  
  // Skip precompilation if array-of-objects present
  if (!hasConditionalFields && !hasArrayOfObjects && /* other checks */) {
    this.precompiledValidator = SchemaPrecompiler.precompileSchema(...);
  }
  // Otherwise use standard validation (which works correctly)
}

๐ŸŽ‰ Result

// After fix - Validation works correctly โœ…
const schema = Interface({
  videos: [{
    id: "string",
    title: "string",
    duration: "number"
  }]
});

// Empty array - PASSES (correct, no minimum constraint)
schema.parse({ videos: [] }); // โœ…

// Valid data - PASSES
schema.parse({ 
  videos: [{ id: "1", title: "Video", duration: 120 }] 
}); // โœ…

// Missing fields - FAILS (correct)
schema.parse({ videos: [{ id: "1" }] }); 
// โŒ Error: Missing required field: title

// Wrong types - FAILS (correct)
schema.parse({ videos: [{ id: 123, title: "Test", duration: "wrong" }] }); 
// โŒ Error: Expected String, but received Number

// Not an array - FAILS (correct)
schema.parse({ videos: { id: "1" } }); 
// โŒ Error: Expected Array, but received Object

โœ… Test Results

Test Case Before After Status
Empty array PASS PASS โœ… Correct
Valid array elements PASS PASS โœ… Correct
Missing required fields PASS FAIL โœ… Fixed
Wrong types PASS FAIL โœ… Fixed
Not an array PASS FAIL โœ… Fixed
TypeScript type inference Tuple Array โœ… Fixed

๐Ÿ“‹ Migration Guide

Good news! These fixes are backward compatible. No changes needed to your existing code.

  • Empty arrays remain valid by default (use array length constraints if you need minimums)
  • TypeScript types are now more accurate
  • Validation is now stricter and catches errors that were previously missed