From 72d13e1bc43966c3b504e3d90bff9860c2453c35 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 2 Jan 2026 02:49:02 +0900 Subject: [PATCH] Revision 0.34.46 (#4) * Functions Must Support TObject as Generic Constraint * ChangeLog * Version --- changelog/0.34.0.md | 2 ++ package-lock.json | 19 +++++------ package.json | 4 +-- src/type/object/object.ts | 45 +++++++++++++++++++------- test/static/assert.ts | 26 +++++++++++++++ test/static/object.ts | 67 +++++++++++++++++++++++++++++++++++++-- 6 files changed, 137 insertions(+), 26 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index a6e050f..474a01f 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.46](https://github.com/sinclairzx81/typebox-legacy/pull/4) + - Ensure TObject Instances remain Covariant with TObject Types. - [Revision 0.34.45](https://github.com/sinclairzx81/typebox-legacy/pull/2) - Documentation Update - Resolve Readme Assets to TypeBox Legacy Repository. - [Revision 0.34.44](https://github.com/sinclairzx81/typebox-legacy/pull/2) diff --git a/package-lock.json b/package-lock.json index ff6fe0f..4ce77e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.42", + "version": "0.34.45", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.42", + "version": "0.34.45", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.9.2" + "typescript": "^5.9.3" } }, "node_modules/@andrewbranch/untar.js": { @@ -1817,10 +1817,11 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3229,9 +3230,9 @@ "dev": true }, "typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index b6d2d05..b1c9320 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.45", + "version": "0.34.46", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -39,6 +39,6 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.9.2" + "typescript": "^5.9.3" } } diff --git a/src/type/object/object.ts b/src/type/object/object.ts index 5459eeb..054b05c 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -63,18 +63,39 @@ type ObjectStatic = ObjectStaticProp // ------------------------------------------------------------------ export type TPropertyKey = string | number // Consider making this PropertyKey export type TProperties = Record -// ---------------------------------------------------------------------------- -// Required -// ---------------------------------------------------------------------------- -/** Creates a RequiredArray derived from the given TProperties value. */ -type TRequiredArray< - Properties extends TProperties, - RequiredProperties extends TProperties = { - [Key in keyof Properties as Properties[Key] extends TOptional ? never : Key]: Properties[Key] - }, - RequiredKeys extends string[] = UnionToTuple>, - Result extends string[] | undefined = RequiredKeys extends [] ? undefined : RequiredKeys, -> = Result +// ------------------------------------------------------------------ +// TRequiredArray +// +// Note: Generating the RequiredArray from TProperties enables TB 1.0 +// to infer TObject via the XSchema inference path. The string[] | +// undefined fallback ensures that TObject remains covariant with +// varying TObject instances. +// +// ------------------------------------------------------------------ +// prettier-ignore +type TIsLiteralString = ( + [Type] extends [string] + ? [string] extends [Type] + ? false + : true + : false +) +// prettier-ignore +type IsRequiredArrayLiteralConstant = ( + RequiredTuple extends [infer Left extends string, ...infer _ extends string[]] + ? TIsLiteralString + : false +) +// prettier-ignore +type TRequiredArray ? never : Key]: Properties[Key] }, + RequiredUnion extends string = Extract, + RequiredTuple extends string[] = UnionToTuple, + Result extends string[] | undefined = ( + IsRequiredArrayLiteralConstant extends true + ? RequiredTuple + : string[] | undefined + )> = Result /** Creates a RequiredArray derived from the given TProperties value. */ function RequiredArray(properties: Properties): TRequiredArray { return globalThis.Object.keys(properties).filter((key) => !IsOptional(properties[key])) as never diff --git a/test/static/assert.ts b/test/static/assert.ts index 862731d..9860869 100644 --- a/test/static/assert.ts +++ b/test/static/assert.ts @@ -68,3 +68,29 @@ export function Expect(schema: T) { ToStaticEncode() {}, } as ExpectResult } +// ------------------------------------------------------------------ +// IsNeverExtends +// ------------------------------------------------------------------ +/** Special case for left-side `never` as given by `(never extends T ? true : false)` */ +export function IsExtendsWhenLeftIsNever(_expect: [TExtendsExpect] extends [true] ? true : false) {} + +// ------------------------------------------------------------------ +// IsExtends +// ------------------------------------------------------------------ +type TExtendsExpect = Left extends Right ? true : false + +export function IsExtends(_expect: TExtendsExpect) {} + +// ------------------------------------------------------------------ +// IsExtendsMutual +// ------------------------------------------------------------------ +type TExtendsMutualExpect = (() => T extends Left ? 1 : 2) extends () => T extends Right ? 1 : 2 ? true : false + +export function IsExtendsMutual(_expect: TExtendsMutualExpect) {} + +// ------------------------------------------------------------------ +// IsExtendsNever +// ------------------------------------------------------------------ +type TExtendsNever = [Type] extends [never] ? true : false + +export function IsExtendsNever(_expect: TExtendsNever) {} diff --git a/test/static/object.ts b/test/static/object.ts index d7f4b0e..e6c6468 100644 --- a/test/static/object.ts +++ b/test/static/object.ts @@ -1,6 +1,5 @@ -import { Expect } from './assert' -import { Type } from '@sinclair/typebox' - +import { Expect, IsExtendsMutual } from './assert' +import { Type, type TObject, type Static, type TNumber } from '@sinclair/typebox' { const T = Type.Object({ A: Type.String(), @@ -68,3 +67,65 @@ import { Type } from '@sinclair/typebox' C: number }>() } +// ------------------------------------------------------------------ +// Required +// ------------------------------------------------------------------ +{ + const _A = Type.Object({}) + const _B = Type.Object({ x: Type.Number() }) + const _C = Type.Object({ x: Type.Number(), y: Type.Number() }) + + type A = (typeof _A)['required'] + type B = (typeof _B)['required'] + type C = (typeof _C)['required'] + + IsExtendsMutual(true) + IsExtendsMutual(true) + IsExtendsMutual(true) +} +// ------------------------------------------------------------------ +// https://github.com/sinclairzx81/typebox/issues/1500 +// ------------------------------------------------------------------ +{ + function test(type: Type): Static { + return null as never + } + const _A = test(Type.Object({})) + const _B = test( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + const _C = test( + Type.Partial( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ), + ) +} +// ------------------------------------------------------------------ +// Inference +// ------------------------------------------------------------------ +{ + type T = Static + const T = null as never as TObject<{}> + Expect(T).ToStatic<{}>() +} +{ + type T = Static + const T = null as never as TObject + Expect(T).ToStatic<{ + [x: string]: unknown + [x: number]: unknown + }>() +} +{ + type T = Static + const T = null as never as TObject<{ x: TNumber }> + Expect(T).ToStatic<{ + x: number + }>() +}