Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const Kind = Symbol.for('TypeBox.Kind')
const Hint = Symbol.for('TypeBox.Hint')

const isSpecialProperty = (name: string) =>
/(\ |-|\t|\n|\.)/.test(name) || !isNaN(+name[0])
/(\ |-|\t|\n|\.|\[|\])/.test(name) || !isNaN(+name[0])

const joinProperty = (v1: string, v2: string | number, isOptional = false) => {
if (typeof v2 === 'number') return `${v1}[${v2}]`
Expand Down Expand Up @@ -122,7 +122,7 @@ const handleRecord = (
const optionals = instruction.optionalsInArray[i + 1]
if (optionals)
for (let oi = 0; oi < optionals.length; oi++) {
const target = `ar${i}v[ar${i}s[i]].${optionals[oi]}`
const target = `ar${i}v[ar${i}s[i]]${optionals[oi]}`

v += `;if(${target}===undefined)delete ${target}`
}
Expand Down Expand Up @@ -358,7 +358,26 @@ const mirror = (
const index = instruction.array

if (property.startsWith('ar')) {
const refName = name.slice(name.indexOf('.') + 1)
const dotIndex = name.indexOf('.')
let refName
if (dotIndex >= 0) {
// Has a dot, extract from the dot onwards
refName = name.slice(dotIndex)
} else {
// No dot, must be bracket notation
refName = name.slice(property.length)
}
// Normalize optional chaining for deletion code
// ?.field -> .field, ?.["field"] -> ["field"]
if (refName.startsWith('?.')) {
if (refName.charAt(2) === '[') {
// Bracket notation: ?.["x"] -> ["x"]
refName = refName.slice(2)
} else {
// Dot notation: ?.x -> .x
refName = refName.slice(1)
}
}
Comment thread
x04 marked this conversation as resolved.
const array = instruction.optionalsInArray

if (array[index]) array[index].push(refName)
Expand Down Expand Up @@ -442,7 +461,7 @@ const mirror = (
// since pointer is checked in object case with ternary as undefined, this is not need
// const pointer = `ar${i}p.${optionals[oi]}`

const target = `ar${i}v[i].${optionals[oi]}`
const target = `ar${i}v[i]${optionals[oi]}`

// we can add semi-colon here because it delimit recursive mirror
v += `;if(${target}===undefined)delete ${target}`
Expand Down
25 changes: 25 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,4 +306,29 @@ describe('Core', () => {

isEqual(shape, value, expected)
})

it('handle bracket notation in property names', () => {
const shape = t.Object({
'order[createdAt]': t.Optional(
t.Union([t.Literal('asc'), t.Literal('desc')])
),
'orderBy[createdAt]': t.Optional(
t.Union([t.Literal('asc'), t.Literal('desc')])
)
})

const value = {
'order[createdAt]': 'asc',
'orderBy[createdAt]': 'desc',
// @ts-expect-error
additional: 'b'
} satisfies typeof shape.static

const expected = {
'order[createdAt]': 'asc',
'orderBy[createdAt]': 'desc'
} satisfies typeof shape.static

isEqual(shape, value, expected)
})
})