sinclairzx81 / typebox

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

Utility type inference appears missing or broken #834

Closed Xhale1 closed 5 months ago

Xhale1 commented 5 months ago

Hello hello! Fantastic library, I've been following this project for a while!

Not entirely sure if this is a bug or intended behavior. Using the Pick example from the docs:

const T = Type.Pick(
  Type.Object({
    x: Type.Number(),
    y: Type.Number(),
  }),
  ['x'],
);

I would expect ['x'] to by typed in such a way that the keys of the object are inferred as accepting 'x' | 'y', allowing type hints on what keys can be picked. Instead, this array accepts any string value. The type appears to be TMappedKey.

I'm using TypeBox 0.32.20 and TypeScript 5.4.5.

sinclairzx81 commented 5 months ago

@Xhale1 Hi, thanks for reporting.

Yes, this is expected behavior. The inference of "pickable" (or "omittable") keys was removed on 0.32.0 to make generic composition a bit simpler. Previously, proving the compiler that keys were legitimately of keyof TObject['properties'] tended to be quite difficult when using TPick and TOmit in generic composition (i.e. passing keys as generic arguments). In addition, the update was also done as a general optimization to prevent the compiler having to introspect pickable properties within the context deeply nested types (which had led to deep instantiation issues in some cases)

The 0.32.0 update removes auto inference of pick-able keys in favor of just omitting any keys not present on picked type. The return type for TPick should be a same as the input type with only the picked keys.

TypeScript Link Here

import { Type } from '@sinclair/typebox'

const T = Type.Pick( // Hover T
  Type.Object({
    x: Type.Number(),
    y: Type.Number(),
  }),
  ['x', 'z'], // no error on 'z'
);

// Which is mapped to the following object type (where the non-existent key 'z' is omitted)
//
// type T: TObject<{
//   x: TNumber;
// }>

The removal of auto inferenced pickable keys was a bit of a trade off in 0.32.0, but was eventually dropped to help simplify composition and bolster inference performance. It was also done to help some of TypeBox's internals generalize better (specifically for Mapped Types (added on 0.32.0) where Pick and Omit may be implemented by way Mapped types in future)

Hope this brings some insight Cheers! S

sinclairzx81 commented 5 months ago

@Xhale1 Hiya,

Might close off this issue as things are working as per design here.

I may revisit auto inference of pick-able properties again in later revisions if I can get the inference fast enough and avoid some of the generics / compositional issues when using Pick/Omit in mapping functions. I do appreciate it can provide a nicer DX, but I think for now, I'm opting to avoid any additional performance overhead and go with the current design until some of the other internal aspects on 0.32.x have stabilized.

Again, hope this brings some insight. All the best! S