{"record":{"id":"dbb2bca4ada53e00","repo":"can1357/oh-my-pi","slug":"intersection-of-distinct-morphs-is-indeterminate","errorCode":null,"errorMessage":"intersection of distinct morphs is indeterminate","messagePattern":"intersection of distinct morphs is indeterminate","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/type.ts","lineNumber":2009,"sourceCode":"\t\t\tk: \"alias\",\n\t\t\tname: a.k === \"alias\" ? a.name : b.k === \"alias\" ? b.name : \"intersection\",\n\t\t\tresolve: () =>\n\t\t\t\t(resolved ??= intersectResolved(a.k === \"alias\" ? a.resolve() : a, b.k === \"alias\" ? b.resolve() : b)),\n\t\t};\n\t\tcache.set(b, reference);\n\t\treturn reference;\n\t}\n\treturn intersectResolved(a, b);\n}\n\nfunction intersectResolved(a: IR, b: IR): IR {\n\tif (a.k === \"never\" || b.k === \"never\") throw new OmpTypeError(\"intersection with never is unsatisfiable\");\n\tif (a.k === \"unknown\") return b;\n\tif (b.k === \"unknown\") return a;\n\tif (a === b) return a;\n\tif (a.k === \"morph\" && b.k === \"morph\") {\n\t\tif (a.fn !== b.fn || a.out !== b.out) {\n\t\t\tthrow new OmpTypeError(\"intersection of distinct morphs is indeterminate\");\n\t\t}\n\t\treturn { ...a, input: intersect(a.input, b.input) };\n\t}\n\tif (a.k === \"morph\") return { ...a, input: intersect(a.input, b) };\n\tif (b.k === \"morph\") return { ...b, input: intersect(a, b.input) };\n\tif (a.k === \"sub\" && a.schema.hasSteps) {\n\t\tif (b.k === \"sub\" && b.schema.hasSteps) {\n\t\t\tif (a.schema === b.schema) return a;\n\t\t\tthrow new OmpTypeError(\"intersection of distinct morphs is indeterminate\");\n\t\t}\n\t\tconst schema = a.schema as InternalType;\n\t\treturn embed(makeType(intersect(schema.ir, b), schema[kSteps], metaOf(schema)));\n\t}\n\tif (b.k === \"sub\" && b.schema.hasSteps) return intersect(b, a);\n\tif (a.k === \"union\" || b.k === \"union\") {\n\t\tconst union = a.k === \"union\" ? a : b.k === \"union\" ? b : undefined;\n\t\tif (union === undefined) throw new OmpTypeError(\"union intersection invariant failed\");\n\t\tconst branches = union.members;","sourceCodeStart":1991,"sourceCodeEnd":2027,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/type.ts#L1991-L2027","documentation":"Intersecting two morphs (types with in/out transformation functions, e.g. string coerced to number) is only defined when both sides are the same morph with the same output. Different functions or outputs would make the resulting input/output ordering ambiguous, so omptype refuses with 'indeterminate' rather than picking an arbitrary order.","triggerScenarios":"intersect(number(string), number(bigintString)) or intersect(morph(f), morph(g)) with f !== g; intersecting two 'sub' schemas backed by different step chains also lands here.","commonSituations":"Combining two independently defined coercion schemas (e.g. two different string→number parsers) in an allOf/intersection; library defaults plus user overrides both installing a morph on the same field.","solutions":["Use the identical morph instance/function on both sides of the intersection","Pre-compose: apply one morph then refine with a plain predicate instead of intersecting two morphs","Refactor so only one side performs the transformation and the other is a refinement of the output type","If merging schemas (e.g. config + overrides), strip one side's morph before intersecting"],"exampleFix":"// before\nintersect(number(string), number(coerceBigint))\n// after\nnumber(string).refine(n => Number.isSafeInteger(n))","handlingStrategy":"try-catch","validationCode":"function morphsCompatible(a, b) {\n  return a.ir?.k !== 'morph' || b.ir?.k !== 'morph' || (a.ir.fn === b.ir.fn && a.ir.out === b.ir.out);\n}","typeGuard":"function isMorphSchema(t): boolean {\n  return t != null && t.ir?.k === 'morph';\n}","tryCatchPattern":"try {\n  return intersect(a, b);\n} catch (err) {\n  if (err instanceof OmpTypeError && err.message.includes('morphs is indeterminate')) {\n    throw new Error('two different morphs on one field — pick a single transformation');\n  }\n  throw err;\n}","preventionTips":["Share one morph instance for a given coercion across modules","Prefer refine() over a second morph when combining constraints","When merging schema sets, strip duplicate morphs from one side first"],"tags":["omptype","morph","intersection","indeterminate"],"backgroundTag":"indeterminate-morph-intersection","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}