Skip to content

Commit

Permalink
Revision 0.33.2 (#947)
Browse files Browse the repository at this point in the history
* Retain Interior Properties on Pick, Omit and Mapped Types

* Version
  • Loading branch information
sinclairzx81 authored Aug 9, 2024
1 parent b00293e commit c489464
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 21 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.33.1",
"version": "0.33.2",
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",
Expand Down
22 changes: 12 additions & 10 deletions src/type/mapped/mapped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ type FromSchemaType<K extends PropertyKey, T extends TSchema> = (
)
// prettier-ignore
function FromSchemaType<K extends PropertyKey, T extends TSchema>(K: K, T: T): FromSchemaType<K, T> {
// required to retain user defined options for mapped type
const options = { ...T }
return (
// unevaluated modifier types
IsOptional(T) ? Optional(FromSchemaType(K, Discard(T, [OptionalKind]) as TSchema)) :
Expand All @@ -213,16 +215,16 @@ function FromSchemaType<K extends PropertyKey, T extends TSchema>(K: K, T: T): F
IsMappedResult(T) ? FromMappedResult(K, T.properties) :
IsMappedKey(T) ? FromMappedKey(K, T.keys) :
// unevaluated types
IsConstructor(T) ? Constructor(FromRest(K, T.parameters), FromSchemaType(K, T.returns)) :
IsFunction(T) ? FunctionType(FromRest(K, T.parameters), FromSchemaType(K, T.returns)) :
IsAsyncIterator(T) ? AsyncIterator(FromSchemaType(K, T.items)) :
IsIterator(T) ? Iterator(FromSchemaType(K, T.items)) :
IsIntersect(T) ? Intersect(FromRest(K, T.allOf)) :
IsUnion(T) ? Union(FromRest(K, T.anyOf)) :
IsTuple(T) ? Tuple(FromRest(K, T.items ?? [])) :
IsObject(T) ? Object(FromProperties(K, T.properties)) :
IsArray(T) ? Array(FromSchemaType(K, T.items)) :
IsPromise(T) ? Promise(FromSchemaType(K, T.item)) :
IsConstructor(T) ? Constructor(FromRest(K, T.parameters), FromSchemaType(K, T.returns), options) :
IsFunction(T) ? FunctionType(FromRest(K, T.parameters), FromSchemaType(K, T.returns), options) :
IsAsyncIterator(T) ? AsyncIterator(FromSchemaType(K, T.items), options) :
IsIterator(T) ? Iterator(FromSchemaType(K, T.items), options) :
IsIntersect(T) ? Intersect(FromRest(K, T.allOf), options) :
IsUnion(T) ? Union(FromRest(K, T.anyOf), options) :
IsTuple(T) ? Tuple(FromRest(K, T.items ?? []), options) :
IsObject(T) ? Object(FromProperties(K, T.properties), options) :
IsArray(T) ? Array(FromSchemaType(K, T.items), options) :
IsPromise(T) ? Promise(FromSchemaType(K, T.item), options) :
T
) as never
}
Expand Down
6 changes: 2 additions & 4 deletions src/type/omit/omit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function OmitResolve<T extends TSchema, K extends PropertyKey[]>(T: T, K: [...K]
return (
IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) :
IsUnion(T) ? Union(FromUnion(T.anyOf, K)) :
IsObject(T) ? Object(FromProperties(T.properties, K)) :
IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) :
Object({})
) as never
}
Expand Down Expand Up @@ -121,7 +121,5 @@ export function Omit(T: TSchema, K: any, options?: SchemaOptions): any {
if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options)
// non-mapped
const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[])
const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema
const R = OmitResolve(T, I)
return CreateType({ ...D, ...R }, options)
return CreateType(OmitResolve(T, I), options)
}
6 changes: 2 additions & 4 deletions src/type/pick/pick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function PickResolve<T extends TSchema, K extends PropertyKey[]>(T: T, K: [...K]
return (
IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) :
IsUnion(T) ? Union(FromUnion(T.anyOf, K)) :
IsObject(T) ? Object(FromProperties(T.properties, K)) :
IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) :
Object({})
) as never
}
Expand All @@ -112,7 +112,5 @@ export function Pick(T: TSchema, K: any, options?: SchemaOptions): any {
if (IsMappedResult(T)) return PickFromMappedResult(T, K, options)
// non-mapped
const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[])
const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema
const R = PickResolve(T, I)
return CreateType({ ...D, ...R }, options)
return CreateType(PickResolve(T, I), options)
}
31 changes: 31 additions & 0 deletions test/runtime/type/guard/type/omit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,35 @@ describe('guard/type/TOmit', () => {
const R = Type.Omit(S, ['x'])
Assert.IsFalse(TransformKind in R)
})
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/944
// ----------------------------------------------------------------
it('Should retain interior properties 1', () => {
const A = Type.Object({ x: Type.Number() }, { additionalProperties: false })
const T = Type.Omit(A, ['x'])
Assert.IsFalse(T.additionalProperties as boolean)
})
it('Should retain interior properties 2', () => {
const A = Type.Object({ x: Type.Number() }, { additionalProperties: false })
const B = Type.Object({ y: Type.Number() }, { additionalProperties: false })
const U = Type.Union([A, B])
const T = Type.Omit(U, ['x'])
Assert.IsFalse(T.anyOf[0].additionalProperties as boolean)
Assert.IsFalse(T.anyOf[1].additionalProperties as boolean)
})
it('Should retain interior properties 3', () => {
const A = Type.Object({ x: Type.Number() }, { additionalProperties: false })
const B = Type.Object({ y: Type.Number() }, { additionalProperties: false })
const U = Type.Intersect([A, B])
const T = Type.Omit(U, ['x'])
Assert.IsFalse(T.allOf[0].additionalProperties as boolean)
Assert.IsFalse(T.allOf[1].additionalProperties as boolean)
})
it('Should retain interior properties 4', () => {
const A = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false })
const T = Type.Mapped(Type.TemplateLiteral('${x|y|z}'), (_) => Type.Omit(A, ['x']))
Assert.IsFalse(T.properties.x.additionalProperties as boolean)
Assert.IsFalse(T.properties.y.additionalProperties as boolean)
Assert.IsFalse(T.properties.z.additionalProperties as boolean)
})
})
31 changes: 31 additions & 0 deletions test/runtime/type/guard/type/pick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,35 @@ describe('guard/type/TPick', () => {
const R = Type.Pick(S, ['x'])
Assert.IsFalse(TransformKind in R)
})
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/944
// ----------------------------------------------------------------
it('Should retain interior properties 1', () => {
const A = Type.Object({ x: Type.Number() }, { additionalProperties: false })
const T = Type.Pick(A, ['x'])
Assert.IsFalse(T.additionalProperties as boolean)
})
it('Should retain interior properties 2', () => {
const A = Type.Object({ x: Type.Number() }, { additionalProperties: false })
const B = Type.Object({ y: Type.Number() }, { additionalProperties: false })
const U = Type.Union([A, B])
const T = Type.Pick(U, ['x'])
Assert.IsFalse(T.anyOf[0].additionalProperties as boolean)
Assert.IsFalse(T.anyOf[1].additionalProperties as boolean)
})
it('Should retain interior properties 3', () => {
const A = Type.Object({ x: Type.Number() }, { additionalProperties: false })
const B = Type.Object({ y: Type.Number() }, { additionalProperties: false })
const U = Type.Intersect([A, B])
const T = Type.Pick(U, ['x'])
Assert.IsFalse(T.allOf[0].additionalProperties as boolean)
Assert.IsFalse(T.allOf[1].additionalProperties as boolean)
})
it('Should retain interior properties 4', () => {
const A = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false })
const T = Type.Mapped(Type.TemplateLiteral('${x|y|z}'), (_) => Type.Pick(A, ['x']))
Assert.IsFalse(T.properties.x.additionalProperties as boolean)
Assert.IsFalse(T.properties.y.additionalProperties as boolean)
Assert.IsFalse(T.properties.z.additionalProperties as boolean)
})
})

0 comments on commit c489464

Please sign in to comment.