Revision 0.31.11 (#575)

* Resolve for Single String Enum

* Version

* Update TEnum to accept TEnumRecord as Generic Argument

* Use TEnum<T> as Assert
This commit is contained in:
sinclairzx81
2023-09-06 00:46:25 +09:00
committed by GitHub
parent 276ddeb443
commit fc0bb0e9f2
5 changed files with 47 additions and 24 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@sinclair/typebox",
"version": "0.31.10",
"version": "0.31.11",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@sinclair/typebox",
"version": "0.31.10",
"version": "0.31.11",
"license": "MIT",
"devDependencies": {
"@sinclair/hammer": "^0.17.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sinclair/typebox",
"version": "0.31.10",
"version": "0.31.11",
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",
+7 -7
View File
@@ -340,10 +340,10 @@ 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 ? TLiteral<T> : never
export type TEnumToLiteralUnion<T extends TEnumValue> = T extends TEnumValue ? TLiteral<T> : never // Note: Empty enums infer as TLiteral<string>
export type TEnumToLiteralTuple<T extends TEnumValue> = UnionToTuple<TEnumToLiteralUnion<T>>
export type TEnumToUnion<T extends TEnumValue, R = UnionType<AssertRest<TEnumToLiteralTuple<T>>>> = R extends TLiteralString ? TNever : R // Note: Empty enum evaluates as TLiteralString
export type TEnum<T extends TEnumValue> = Ensure<TEnumToUnion<T>>
export type TEnumToUnion<T extends TEnumValue> = UnionType<AssertRest<TEnumToLiteralTuple<T>>>
export type TEnum<T extends TEnumRecord> = TEnumToUnion<T[keyof T]>
// --------------------------------------------------------------------------
// TExtends
// --------------------------------------------------------------------------
@@ -2897,13 +2897,13 @@ export class JsonTypeBuilder extends TypeBuilder {
return Type.Object(properties, options) as TComposite<T>
}
/** `[Json]` Creates a Enum type */
public Enum<V extends TEnumValue, T extends Record<TEnumKey, V>>(item: T, options: SchemaOptions = {}): TEnum<T[keyof T]> {
if (ValueGuard.IsUndefined(item)) return this.Union([], options) as TEnum<T[keyof T]>
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)] // distinct
const values2 = [...new Set(values1)]
const anyOf = values2.map((value) => Type.Literal(value))
return this.Union(anyOf, options) as TEnum<T[keyof T]>
return this.Union(anyOf, options) 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> {
+35 -12
View File
@@ -42,10 +42,24 @@ describe('type/guard/TEnum', () => {
const T = Type.Enum(E)
Assert.IsTrue(TypeGuard.TNever(T))
})
// ----------------------------------------------------------------
// Enum
// ----------------------------------------------------------------
it('Should guard for TEnum Enum 0', () => {
enum E {}
const T = Type.Enum(E)
Assert.IsTrue(TypeGuard.TNever(T))
})
it('Should guard for TEnum Enum 1', () => {
enum E {
A,
}
const T = Type.Enum(E)
Assert.IsTrue(TypeGuard.TLiteral(T))
Assert.IsEqual(T.const, E.A)
})
it('Should guard for TEnum Enum 2', () => {
enum E {
A = 1,
B = 2,
@@ -53,11 +67,11 @@ describe('type/guard/TEnum', () => {
}
const T = Type.Enum(E)
Assert.IsTrue(TypeGuard.TUnion(T))
Assert.IsEqual(T.anyOf[0].const, 1)
Assert.IsEqual(T.anyOf[1].const, 2)
Assert.IsEqual(T.anyOf[2].const, 3)
Assert.IsEqual(T.anyOf[0].const, E.A)
Assert.IsEqual(T.anyOf[1].const, E.B)
Assert.IsEqual(T.anyOf[2].const, E.C)
})
it('Should guard for TEnum Enum 2', () => {
it('Should guard for TEnum Enum 3', () => {
enum E {
A = 'X',
B = 'Y',
@@ -65,11 +79,11 @@ describe('type/guard/TEnum', () => {
}
const T = Type.Enum(E)
Assert.IsTrue(TypeGuard.TUnion(T))
Assert.IsEqual(T.anyOf[0].const, 'X')
Assert.IsEqual(T.anyOf[1].const, 'Y')
Assert.IsEqual(T.anyOf[2].const, 'Z')
Assert.IsEqual(T.anyOf[0].const, E.A)
Assert.IsEqual(T.anyOf[1].const, E.B)
Assert.IsEqual(T.anyOf[2].const, E.C)
})
it('Should guard for TEnum Enum 3', () => {
it('Should guard for TEnum Enum 4', () => {
enum E {
A = 'X',
B = 'Y',
@@ -77,14 +91,23 @@ describe('type/guard/TEnum', () => {
}
const T = Type.Enum(E)
Assert.IsTrue(TypeGuard.TUnion(T))
Assert.IsEqual(T.anyOf[0].const, 'X')
Assert.IsEqual(T.anyOf[1].const, 'Y')
Assert.IsEqual(T.anyOf[0].const, E.A)
Assert.IsEqual(T.anyOf[1].const, E.B)
Assert.IsEqual(T.anyOf.length, 2)
})
// ----------------------------------------------------------------
// Object Literal
// ----------------------------------------------------------------
it('Should guard for TEnum Object Literal 0', () => {
const T = Type.Enum({})
Assert.IsTrue(TypeGuard.TNever(T))
})
it('Should guard for TEnum Object Literal 1', () => {
const T = Type.Enum({ A: 1 })
Assert.IsTrue(TypeGuard.TLiteral(T))
Assert.IsEqual(T.const, 1)
})
it('Should guard for TEnum Object Literal 2', () => {
const T = Type.Enum({
A: 1,
B: 2,
@@ -95,7 +118,7 @@ describe('type/guard/TEnum', () => {
Assert.IsEqual(T.anyOf[1].const, 2)
Assert.IsEqual(T.anyOf[2].const, 3)
})
it('Should guard for TEnum Object Literal 2', () => {
it('Should guard for TEnum Object Literal 3', () => {
const T = Type.Enum({
A: 'X',
B: 'Y',
@@ -106,7 +129,7 @@ describe('type/guard/TEnum', () => {
Assert.IsEqual(T.anyOf[1].const, 'Y')
Assert.IsEqual(T.anyOf[2].const, 'Z')
})
it('Should guard for TEnum Object Literal 3', () => {
it('Should guard for TEnum Object Literal 4', () => {
const T = Type.Enum({
A: 'X',
B: 'Y',
+2 -2
View File
@@ -30,10 +30,10 @@ import { Type } from '@sinclair/typebox'
Expect(T).ToStatic<1 | 2>()
}
{
// expect empty enum to be never
// 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