Skip to content

Commit

Permalink
Revision 0.32.8 (#728)
Browse files Browse the repository at this point in the history
- Clone Default Annotation on Assignment
  • Loading branch information
sinclairzx81 authored Jan 10, 2024
1 parent 8e1bc04 commit 7ea2956
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 39 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sinclair/typebox",
"version": "0.32.7",
"version": "0.32.8",
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",
Expand Down
2 changes: 1 addition & 1 deletion src/value/cast/cast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function SelectUnion(union: TUnion, references: TSchema[], value: any): TSchema
}
function CastUnion(union: TUnion, references: TSchema[], value: any) {
if ('default' in union) {
return union.default
return typeof value === 'function' ? union.default : Clone(union.default)
} else {
const schema = SelectUnion(union, references, value)
return Cast(schema, references, value)
Expand Down
77 changes: 42 additions & 35 deletions src/value/create/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ THE SOFTWARE.

import { HasPropertyKey, IsString } from '../guard/index'
import { Check } from '../check/index'
import { Clone } from '../clone/index'
import { Deref } from '../deref/index'
import { TemplateLiteralGenerate, IsTemplateLiteralFinite } from '../../type/template-literal/index'
import { PatternStringExact, PatternNumberExact } from '../../type/patterns/index'
Expand Down Expand Up @@ -78,11 +79,17 @@ export class ValueCreateError extends TypeBoxError {
}
}
// ------------------------------------------------------------------
// Default
// ------------------------------------------------------------------
function FromDefault(value: unknown) {
return typeof value === 'function' ? value : Clone(value)
}
// ------------------------------------------------------------------
// Create
// ------------------------------------------------------------------
function FromAny(schema: TAny, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return {}
}
Expand All @@ -93,7 +100,7 @@ function FromArray(schema: TArray, references: TSchema[]): any {
} else if ('contains' in schema && !HasPropertyKey(schema, 'default')) {
throw new ValueCreateError(schema, 'Array with the contains constraint requires a default value')
} else if ('default' in schema) {
return schema.default
return FromDefault(schema.default)
} else if (schema.minItems !== undefined) {
return Array.from({ length: schema.minItems }).map((item) => {
return Visit(schema.items, references)
Expand All @@ -104,28 +111,28 @@ function FromArray(schema: TArray, references: TSchema[]): any {
}
function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return (async function* () {})()
}
}
function FromBigInt(schema: TBigInt, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return BigInt(0)
}
}
function FromBoolean(schema: TBoolean, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return false
}
}
function FromConstructor(schema: TConstructor, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
const value = Visit(schema.returns, references) as any
if (typeof value === 'object' && !Array.isArray(value)) {
Expand All @@ -144,7 +151,7 @@ function FromConstructor(schema: TConstructor, references: TSchema[]): any {
}
function FromDate(schema: TDate, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if (schema.minimumTimestamp !== undefined) {
return new Date(schema.minimumTimestamp)
} else {
Expand All @@ -153,14 +160,14 @@ function FromDate(schema: TDate, references: TSchema[]): any {
}
function FromFunction(schema: TFunction, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return () => Visit(schema.returns, references)
}
}
function FromInteger(schema: TInteger, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if (schema.minimum !== undefined) {
return schema.minimum
} else {
Expand All @@ -169,7 +176,7 @@ function FromInteger(schema: TInteger, references: TSchema[]): any {
}
function FromIntersect(schema: TIntersect, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
// --------------------------------------------------------------
// Note: The best we can do here is attempt to instance each
Expand All @@ -188,42 +195,42 @@ function FromIntersect(schema: TIntersect, references: TSchema[]): any {
}
function FromIterator(schema: TIterator, references: TSchema[]) {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return (function* () {})()
}
}
function FromLiteral(schema: TLiteral, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return schema.const
}
}
function FromNever(schema: TNever, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
throw new ValueCreateError(schema, 'Never types cannot be created. Consider using a default value.')
}
}
function FromNot(schema: TNot, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
throw new ValueCreateError(schema, 'Not types must have a default value')
}
}
function FromNull(schema: TNull, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return null
}
}
function FromNumber(schema: TNumber, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if (schema.minimum !== undefined) {
return schema.minimum
} else {
Expand All @@ -232,11 +239,11 @@ function FromNumber(schema: TNumber, references: TSchema[]): any {
}
function FromObject(schema: TObject, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
const required = new Set(schema.required)
return (
schema.default ||
FromDefault(schema.default) ||
Object.entries(schema.properties).reduce((acc, [key, schema]) => {
return required.has(key) ? { ...acc, [key]: Visit(schema, references) } : { ...acc }
}, {})
Expand All @@ -245,15 +252,15 @@ function FromObject(schema: TObject, references: TSchema[]): any {
}
function FromPromise(schema: TPromise, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return Promise.resolve(Visit(schema.item, references))
}
}
function FromRecord(schema: TRecord, references: TSchema[]): any {
const [keyPattern, valueSchema] = Object.entries(schema.patternProperties)[0]
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if (!(keyPattern === PatternStringExact || keyPattern === PatternNumberExact)) {
const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|')
return propertyKeys.reduce((acc, key) => {
Expand All @@ -265,14 +272,14 @@ function FromRecord(schema: TRecord, references: TSchema[]): any {
}
function FromRef(schema: TRef, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return Visit(Deref(schema, references), references)
}
}
function FromRegExp(schema: TRegExp, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
throw new ValueCreateError(schema, 'RegExp types cannot be created. Consider using a default value.')
}
Expand All @@ -282,17 +289,17 @@ function FromString(schema: TString, references: TSchema[]): any {
if (!HasPropertyKey(schema, 'default')) {
throw new ValueCreateError(schema, 'String types with patterns must specify a default value')
} else {
return schema.default
return FromDefault(schema.default)
}
} else if (schema.format !== undefined) {
if (!HasPropertyKey(schema, 'default')) {
throw new ValueCreateError(schema, 'String types with formats must specify a default value')
} else {
return schema.default
return FromDefault(schema.default)
}
} else {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if (schema.minLength !== undefined) {
// prettier-ignore
return Array.from({ length: schema.minLength }).map(() => ' ').join('')
Expand All @@ -303,7 +310,7 @@ function FromString(schema: TString, references: TSchema[]): any {
}
function FromSymbol(schema: TSymbol, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if ('value' in schema) {
return Symbol.for(schema.value)
} else {
Expand All @@ -312,7 +319,7 @@ function FromSymbol(schema: TSymbol, references: TSchema[]): any {
}
function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[]) {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
}
if (!IsTemplateLiteralFinite(schema)) throw new ValueCreateError(schema, 'Can only create template literals that produce a finite variants. Consider using a default value.')
const generated = TemplateLiteralGenerate(schema) as string[]
Expand All @@ -321,14 +328,14 @@ function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[]) {
function FromThis(schema: TThis, references: TSchema[]): any {
if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateError(schema, 'Cannot create recursive type as it appears possibly infinite. Consider using a default.')
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return Visit(Deref(schema, references), references)
}
}
function FromTuple(schema: TTuple, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
}
if (schema.items === undefined) {
return []
Expand All @@ -338,14 +345,14 @@ function FromTuple(schema: TTuple, references: TSchema[]): any {
}
function FromUndefined(schema: TUndefined, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return undefined
}
}
function FromUnion(schema: TUnion, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if (schema.anyOf.length === 0) {
throw new Error('ValueCreate.Union: Cannot create Union with zero variants')
} else {
Expand All @@ -354,7 +361,7 @@ function FromUnion(schema: TUnion, references: TSchema[]): any {
}
function FromUint8Array(schema: TUint8Array, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else if (schema.minByteLength !== undefined) {
return new Uint8Array(schema.minByteLength)
} else {
Expand All @@ -363,21 +370,21 @@ function FromUint8Array(schema: TUint8Array, references: TSchema[]): any {
}
function FromUnknown(schema: TUnknown, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return {}
}
}
function FromVoid(schema: TVoid, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
return void 0
}
}
function FromKind(schema: TSchema, references: TSchema[]): any {
if (HasPropertyKey(schema, 'default')) {
return schema.default
return FromDefault(schema.default)
} else {
throw new Error('User defined types must specify a default value')
}
Expand Down
15 changes: 15 additions & 0 deletions test/runtime/value/create/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,19 @@ describe('value/create/Object', () => {
z: 3,
})
})
// ----------------------------------------------------------------
// Mutation
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/726
it('Should clone defaults on assignment - no mutation', () => {
const T = Type.Object(
{
x: Type.Number(),
},
{ default: { x: 1 } },
)
const V = Value.Create(T)
V.x = 123
Assert.IsEqual(T.default, { x: 1 })
})
})

0 comments on commit 7ea2956

Please sign in to comment.