Revision 0.29.1

This commit is contained in:
sinclair
2023-07-03 05:09:23 +09:00
parent bdeb24f15f
commit 1504082b96
2 changed files with 51 additions and 13 deletions
+40 -4
View File
@@ -6,6 +6,15 @@ Revision 0.29.0 makes a minor interface and schema representation change to the
As this revision constitutes a breaking representation change for `Type.Not`, a minor semver revision is required.
## Contents
- Enhancements
- [Type.Not Representation Change](#Representation-Change)
- [Not Inversion](#Not-Inversion)
- [Inference Limitations](#Inference-Limitations)
<a name="Representation-Change"></a>
## Type.Not Representation Change
The `Type.Not` was first introduced in Revision 0.26.0. This type accepted two arguments, the first is the `not` type, the second is the `allowed` type. In 0.26.0, TypeBox would treat the `allowed` type as the inferred type with the schema represented in the following form.
@@ -81,11 +90,13 @@ const T = {
//
type T = Static<typeof T> // type T = number
```
The 0.29.0 `Not` type properly represents the JSON Schema `not` keyword in its simplest form, as well as making better use of the type intersection narrowing capabilities of TypeScript with respect to inference.
The 0.29.0 `Not` type properly represents the JSON Schema `not` keyword in its simplest form, as well as making better use of intersection type narrowing capabilities of TypeScript.
### Invert Not
<a name="Not-Inversion"></a>
In revision 0.29.0, it is possible to invert the `Not` type. TypeBox will track each inversion and statically infer appropriately.
## Not Inversion
The not type can be inverted through nesting.
```typescript
// not not string
@@ -105,4 +116,29 @@ const T = {
// inferred as
//
type T = Static<typeof T> // type T = string
```
```
<a name="Inference-Limitations"></a>
## Inference Limitations
Not types are synonymous with the concept of [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. Because of this, it is not currently possible to infer negated types in a way one would naturally expect for some cases. Consider the following.
```typescript
const T = Type.Intersect([Type.String(), Type.Not(Type.String())])
type T = Static<typeof T> // type T = string & not string
// actual: string
// expect: never
```
As such, the use of Not types should be used with some consideration to current limitations, and reserved primarily for narrowing cases such as the following.
```typescript
const T = Type.Intersect([Type.String(), Type.Not(Type.Literal('disallowed string'))])
type T = Static<typeof T> // type T = string & not 'disallowed string'
// actual: string
// expect: string
```
+11 -9
View File
@@ -888,14 +888,16 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = {
### Not Types
Not types are supported with `Type.Not`. This type represents the JSON Schema `not` keyword and will statically infer as `unknown`. Note that Not types are not supported in TypeScript, but can still be partially expressed by interpreting `not` as the broad type `unknown`. When used in intersections, the Not type can be used to create refined assertion rules for specific types, with the inference derived from TypeScript's ability to narrow from `unknown` to `T` via intersection.
For example, consider a type which is `number` but not `1 | 2 | 3` and where the static type would still technically be a `number`. The following shows a pseudo TypeScript example using `not` followed by the TypeBox implementation.
TypeBox has partial support for the JSON schema `not` keyword with `Type.Not`. This type is synonymous with the concept of a [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. TypeBox does provide partial inference support via the intersection of `T & not U` (where all negated types infer as `unknown`). This can be used in the following context.
```typescript
// Pseudo TypeScript
// TypeScript
type T = number & not (1 | 2 | 3) // allow all numbers except 1, 2, 3
type T = Exclude<number, 1 | 2 | 3> // all numbers except 1, 2, 3
//
// ideally expressed as:
//
// type T = number & not (1 | 2 | 3)
// TypeBox
@@ -914,11 +916,11 @@ const T = Type.Intersect([ // const T = {
// ]
// }
type T = Static<typeof T> // evaluates as:
type T = Static<typeof T> // inferred:
//
// type T = (number & (not (1 | 2 | 3)))
// type T = (number & (unknown))
// type T = (number)
// type T = number & not (1 | 2 | 3)
// type T = number & unknown
// type T = number
```
The Not type can be used with constraints to define schematics for types that would otherwise be difficult to express.