sinclairzx81 / typebox

Json Schema Type Builder with Static Type Resolution for TypeScript
Other
5.08k stars 161 forks source link

[0.34.4] T.Record with T.Ref values fails IsSchema check #1087

Closed Bubz43 closed 5 days ago

Bubz43 commented 5 days ago

Migrating from 0.33.22 to 0.34.4 and I was running into problems, managed to narrow it down to Record with Ref value types. It looks like if the Record value type is a Ref it makes it a special Computed which has a note saying it is for use with the new modules feature, but I am relying on the behavior of having these RefRecords in normal non-module schemas.

const RefRecordSchema = T.Record(T.String(), T.Ref("Test"));
console.log("is schema", TypeGuard.IsSchema(RefRecordSchema)); // expect true, is false
sinclairzx81 commented 5 days ago

@Bubz43 Hi! Thanks for reporting.

I have pushed a fix for this on 0.34.5. Just note there has been quite a few updates to TRef in 0.34.x (and the introduction of Module types). The TComputed you saw will be the return type for computable types (Partial, Required, Pick, Omit, etc) whenever these types are passed TRef (and Record types fit the category of computable types)

// Note: A TRef schema consists only of { $ref: 'A' } and contains no information about the target it's 
// referencing. Because Partial needs a set of properties to apply Partial to, 0.34.x will have types of 
// this form this return a TComputed. The TComputed is used to defer the Partial operation until 
// the target type A is known.

const R = Type.Partial(Type.Ref('A')) // TComputed<'Partial', [TRef<'A'>]>

The TComputed is used as a dereferencing mechanism in Modules, allowing for auto deference + compute of types contained within the scope of a Module (this was mostly written to enable mutual recursive schematics + inference)

const Module = Type.Module({

  K: Type.TemplateLiteral('${0|1}${0|1}'),

  V: Type.Object({ x: Type.Number(), y: Type.Number() }),

  // Record Type With Computed / Referenced Parameters
  R: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('V')))
})

const R = Module.Import('R')

type R = Static<typeof R>   // type R = {
                            //   "00": {
                            //       x?: number | undefined;
                            //       y?: number | undefined;
                            //   };
                            //   "01": {
                            //       x?: number | undefined;
                            //       y?: number | undefined;
                            //   };
                            //   10: {
                            //       x?: number | undefined;
                            //       y?: number | undefined;
                            //   };
                            //   11: {
                            //       x?: number | undefined;
                            //       y?: number | undefined;
                            //   };
                            // }

Getting this functionality right will be the focus of 0.34.x. So if you do spot any issues, please let me know. Additional information on this particular fix can be found at the PR link below.

https://github.com/sinclairzx81/typebox/pull/1088


The PR closed off this issue, but if you have any follow up questions on the new functionality, or issues migrating, please let me know, ill help out where I can.

Cheers! S