sinclairzx81 / typebox

Json Schema Type Builder with Static Type Resolution for TypeScript
Other
4.85k stars 155 forks source link

Transform + Map/Index does not work #923

Closed petrkozorezov closed 3 months ago

petrkozorezov commented 3 months ago
import { Value } from '@sinclair/typebox/value'
import * as t from '@sinclair/typebox/type'

export const TypeAsStr =
  <Schema extends t.TSchema>(Schema: Schema) =>
    t.Transform(Schema)
      .Decode(JSON.stringify)
      .Encode(JSON.parse)

const T_ = t.Object({ a: t.Integer() })
const T = t.Mapped(t.KeyOf(T_), (K) =>
  TypeAsStr(t.Index(T_, K))
)
type T = t.Static<typeof T>

const encoded: T = { a: 2 }
const decoded = Value.Decode(T, encoded)
console.log("encoded", encoded)
console.log("decoded", decoded)

Prints

encoded {
  a: 2
}
decoded {
  a: 2
}

And T inferred as {a: unknown}

But code without Map/Index works as expected

import { Value } from '@sinclair/typebox/value'
import * as t from '@sinclair/typebox/type'

export const TypeAsStr =
  <Schema extends t.TSchema>(Schema: Schema) =>
    t.Transform(Schema)
      .Decode(JSON.stringify)
      .Encode(JSON.parse)

const T = t.Object({a: TypeAsStr(t.Integer())})
type T = t.Static<typeof T>

const encoded: T = { a: 2 }
const decoded = Value.Decode(T, encoded)
console.log("encoded", encoded)
console.log("decoded", decoded)

Prints

encoded {
  a: 2
}
decoded {
  a: "2"
}
petrkozorezov commented 3 months ago

I'm sorry, I tried old version, and see it works in 0.32.34.

petrkozorezov commented 3 months ago

No, my mistake, updating to the latest version does not help.

sinclairzx81 commented 3 months ago

@petrkozorezov Hi!

Unfortunately, Mapped Types are not well supported with generic types, nor generic types that return Transforms. The reason is due to the TMappedKey<[...]> parameter requiring evaluation (per key) to generate the mapped result. Because of this, it means the generic function TypeAsStr would need to embed some of the evaluation infrastructure to enable the mapping to work correctly (which is very very complex)

There are future plans to enable better generalization of mapped types in later revisions (likely through HKT's), however as per the current implementation, the infrastructure required to handle the mapping specific on a case by case basis (meaning your TypeAsStr would also need a similar specific mapping), so can't really advise a fix under the current system.

Will close up the issue for now as a known issue / limitation with Mapped Types. But will reinvestigate in subsequent revisions (there should be a significant revision to TB types due near the end of this year).

All the best S

MatthewAry commented 2 months ago

Bummer. Just ran into this myself.