Revision 0.31.14 (#584)

* Revert 0.31.8 Enum

* Intercept for Enum on StaticDecode
This commit is contained in:
sinclairzx81
2023-09-06 05:12:54 +09:00
committed by GitHub
parent 4edc46c2ef
commit 6b27faba28
8 changed files with 73 additions and 16 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@sinclair/typebox",
"version": "0.31.13",
"version": "0.31.14",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@sinclair/typebox",
"version": "0.31.13",
"version": "0.31.14",
"license": "MIT",
"devDependencies": {
"@sinclair/hammer": "^0.17.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sinclair/typebox",
"version": "0.31.13",
"version": "0.31.14",
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",
+9 -7
View File
@@ -176,6 +176,7 @@ export type TAnySchema =
| TBoolean
| TConstructor
| TDate
| TEnum
| TFunction
| TInteger
| TIntersect
@@ -340,11 +341,12 @@ export interface TDate extends TSchema, DateOptions {
export type TEnumRecord = Record<TEnumKey, TEnumValue>
export type TEnumValue = string | number
export type TEnumKey = string
export type TEnumToLiteralUnion<T extends TEnumValue> = T extends TEnumValue ? (string extends T ? TNever : TLiteral<T>) : never
// ^ empty enums evaluate as string
export type TEnumToLiteralTuple<T extends TEnumValue> = UnionToTuple<TEnumToLiteralUnion<T>>
export type TEnumToUnion<T extends TEnumValue> = UnionType<AssertRest<TEnumToLiteralTuple<T>>>
export type TEnum<T extends TEnumRecord> = TEnumToUnion<T[keyof T]>
export interface TEnum<T extends Record<string, string | number> = Record<string, string | number>> extends TSchema {
[Kind]: 'Union'
[Hint]: 'Enum'
static: T[keyof T]
anyOf: TLiteral<T[keyof T]>[]
}
// --------------------------------------------------------------------------
// TExtends
// --------------------------------------------------------------------------
@@ -868,6 +870,7 @@ export type DecodeType<T extends TSchema> = (
T extends TArray<infer S extends TSchema> ? TArray<DecodeType<S>> :
T extends TAsyncIterator<infer S extends TSchema> ? TAsyncIterator<DecodeType<S>> :
T extends TConstructor<infer P extends TSchema[], infer R extends TSchema> ? TConstructor<P, DecodeType<R>> :
T extends TEnum<infer S> ? TEnum<S> : // intercept for union. interior non decodable
T extends TFunction<infer P extends TSchema[], infer R extends TSchema> ? TFunction<P, DecodeType<R>> :
T extends TIntersect<infer S extends TSchema[]> ? TIntersect<DecodeRest<S>> :
T extends TIterator<infer S extends TSchema> ? TIterator<DecodeType<S>> :
@@ -2899,12 +2902,11 @@ export class JsonTypeBuilder extends TypeBuilder {
}
/** `[Json]` Creates a Enum type */
public Enum<V extends TEnumValue, T extends Record<TEnumKey, V>>(item: T, options: SchemaOptions = {}): TEnum<T> {
if (ValueGuard.IsUndefined(item)) return this.Never(options) as TEnum<T>
// prettier-ignore
const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][]
const values2 = [...new Set(values1)]
const anyOf = values2.map((value) => Type.Literal(value))
return this.Union(anyOf, options) as TEnum<T>
return this.Union(anyOf, { ...options, [Hint]: 'Enum' }) as TEnum<T>
}
/** `[Json]` Creates a Conditional type */
public Extends<L extends TSchema, R extends TSchema, T extends TSchema, U extends TSchema>(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends<L, R, T, U> {
+45
View File
@@ -0,0 +1,45 @@
import { Assert } from '../../assert'
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
describe('value/transform/Enum', () => {
enum E {
A,
B,
C,
}
// --------------------------------------------------------
// Identity
// --------------------------------------------------------
const T0 = Type.Transform(Type.Enum(E))
.Decode((value) => value)
.Encode((value) => value)
it('Should decode identity', () => {
const R = Value.Decode(T0, E.A)
Assert.IsEqual(R, E.A)
})
it('Should encode identity', () => {
const R = Value.Encode(T0, E.A)
Assert.IsEqual(R, E.A)
})
it('Should throw on identity decode', () => {
Assert.Throws(() => Value.Decode(T0, null))
})
// --------------------------------------------------------
// Mapped
// --------------------------------------------------------
const T1 = Type.Transform(Type.Enum(E))
.Decode((value) => 1)
.Encode((value) => E.A)
it('Should decode mapped', () => {
const R = Value.Decode(T1, E.A)
Assert.IsEqual(R, 1)
})
it('Should encode mapped', () => {
const R = Value.Encode(T1, null)
Assert.IsEqual(R, E.A)
})
it('Should throw on mapped decode', () => {
Assert.Throws(() => Value.Decode(T1, null))
})
})
+1
View File
@@ -6,6 +6,7 @@ import './bigint'
import './boolean'
import './constructor'
import './date'
import './enum'
import './function'
import './integer'
import './intersect'
+1 -1
View File
@@ -33,7 +33,7 @@ import { Type } from '@sinclair/typebox'
// expect empty enum to be string (as empty enums T[keyof T] evaluates as string)
enum E {}
const T = Type.Enum(E)
Expect(T).ToStaticNever()
Expect(T).ToStatic<string>()
}
{
// expect empty enum to be never
+1 -5
View File
@@ -78,9 +78,5 @@ import { Type, Static } from '@sinclair/typebox'
C = 'Z',
}
const T = Type.Record(Type.Enum(E), Type.Number())
Expect(T).ToStatic<{
X: number
Y: number
Z: number
}>()
Expect(T).ToStatic<{}>()
}
+13
View File
@@ -278,3 +278,16 @@ import { Expect } from './assert'
Expect(T).ToStaticDecode<number>()
Expect(GenericIntersect(T)).ToStaticDecode<1>()
}
{
// should decode enum
enum E {
A,
B,
C,
}
const T = Type.Transform(Type.Enum(E))
.Decode((value) => 1 as const)
.Encode((value) => E.A)
Expect(T).ToStaticDecode<1>()
Expect(T).ToStaticEncode<E>()
}