diff --git a/ark/schema/structure/structure.ts b/ark/schema/structure/structure.ts index fb36b151e..f14f4e83a 100644 --- a/ark/schema/structure/structure.ts +++ b/ark/schema/structure/structure.ts @@ -227,6 +227,10 @@ const implementation: nodeImplementationOf = const lInner = { ...l.inner } const rInner = { ...r.inner } const disjointResult = new Disjoint() + // props an index signature narrows to once the other side's keys + // are known. they can't be added to the side they came from, where + // a prop with the same key would never be merged with them + const derived: nodeOfKind[] = [] if (l.undeclared) { const lKey = l.keyof() for (const k of r.requiredKeys) { @@ -250,18 +254,8 @@ const implementation: nodeImplementationOf = const indexOverlap = intersectNodesRoot(lKey, n.signature, ctx.$) if (indexOverlap instanceof Disjoint) return [] const normalized = normalizeIndex(indexOverlap, n.value, ctx.$) - if (normalized.required) { - rInner.required = conflatenate( - rInner.required, - normalized.required - ) - } - if (normalized.optional) { - rInner.optional = conflatenate( - rInner.optional, - normalized.optional - ) - } + derived.push(...(normalized.required ?? [])) + derived.push(...(normalized.optional ?? [])) return normalized.index ?? [] }) } @@ -289,18 +283,8 @@ const implementation: nodeImplementationOf = const indexOverlap = intersectNodesRoot(rKey, n.signature, ctx.$) if (indexOverlap instanceof Disjoint) return [] const normalized = normalizeIndex(indexOverlap, n.value, ctx.$) - if (normalized.required) { - lInner.required = conflatenate( - lInner.required, - normalized.required - ) - } - if (normalized.optional) { - lInner.optional = conflatenate( - lInner.optional, - normalized.optional - ) - } + derived.push(...(normalized.required ?? [])) + derived.push(...(normalized.optional ?? [])) return normalized.index ?? [] }) @@ -320,7 +304,7 @@ const implementation: nodeImplementationOf = kind: "structure", baseInner, l: flattenConstraints(lInner), - r: flattenConstraints(rInner), + r: [...flattenConstraints(rInner), ...derived], roots: [], ctx }) diff --git a/ark/type/__tests__/objects/onUndeclaredKey.test.ts b/ark/type/__tests__/objects/onUndeclaredKey.test.ts index c500bd6e6..775af685a 100644 --- a/ark/type/__tests__/objects/onUndeclaredKey.test.ts +++ b/ark/type/__tests__/objects/onUndeclaredKey.test.ts @@ -27,6 +27,15 @@ contextualize(() => { }) }) + it("merges a narrowed index signature with a declared key", () => { + const L = type({ a: "number", "[string]": "number" }) + const R = type({ a: "number", b: "number", "+": "reject" }) + attest(L.and(R).expression).snap( + "{ a: number, b: number, + (undeclared): reject }" + ) + attest(R.and(L).expression).equals(L.and(R).expression) + }) + describe("traversal", () => { const getExtraneousB = () => ({ a: "ok", b: "why?" })