sinclairzx81 / typebox

Json Schema Type Builder with Static Type Resolution for TypeScript
Other
4.56k stars 148 forks source link

`Value.Cast` casts to wrong schema when called with union of `Refs` #880

Closed Bubz43 closed 1 month ago

Bubz43 commented 1 month ago

If you have a TUnion<TRef<TObject>[]> and attempt to cast a value into the union, even if it has a matching union discriminator field, it will cast to the wrong union.

import { Type } from "@sinclair/typebox";
import { Cast, Check } from "@sinclair/typebox/value";

const A = Type.Object(
    {
        type: Type.Literal("a"),
    },
    { $id: "a" },
);

const B = Type.Object(
    {
        type: Type.Literal("b"),
        value: Type.Number(),
    },
    { $id: "b" },
);

const b = { type: "b" };

const URefs = Type.Union([Type.Ref(A), Type.Ref(B)]);
const failCast = Cast(URefs, [A, B], b); // Ends up casting to A instead of B
console.log("cast with refs", Check(B, failCast));

const U = Type.Union([A, B]);
const successCast = Cast(U, b); // Cast correctly to B
console.log("cast without refs", Check(B, successCast));

The ScoreUnion function only checks the properties of the schema if it is an TObject schema, and since its a TRef it instead just checks if its correct. Presumably it should first check if its a TRef and attempt to Deref it, and failing that move onto the other steps.

sinclairzx81 commented 1 month ago

@Bubz43 Hi, thanks for reporting. This is a good catch!

Have pushed a fix for this on 0.32.31. If you run into any issues, feel free to ping on this thread.

Thanks again! S