{"record":{"id":"1bd2e1193e48ddc8","repo":"can1357/oh-my-pi","slug":"a-postfix-required-element-cannot-follow-an-option","errorCode":null,"errorMessage":"A postfix required element cannot follow an optional or defaultable element","messagePattern":"A postfix required element cannot follow an optional or defaultable element","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/type.ts","lineNumber":3121,"sourceCode":"\t\t}\n\t\tif (spreadIndexes.length > 1) {\n\t\t\tconst secondSpread = definitions[spreadIndexes[1] + 1];\n\t\t\tif (\n\t\t\t\tArray.isArray(secondSpread) &&\n\t\t\t\tsecondSpread.some(\n\t\t\t\t\telement => typeof element === \"string\" && (element.endsWith(\"?\") || element.includes(\"=\")),\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthrow new OmpTypeError(\"An optional element may not follow a variadic element\");\n\t\t\t}\n\t\t\tthrow new OmpTypeError(\"A tuple may have at most one variadic element\");\n\t\t}\n\t\tif (spreadIndexes.length === 1 && spreadIndexes[0] + 2 < (marker === -1 ? definitions.length : marker)) {\n\t\t\tconst preceding = definitions.slice(0, spreadIndexes[0]);\n\t\t\tif (\n\t\t\t\tpreceding.some(element => typeof element === \"string\" && (element.endsWith(\"?\") || /\\s=\\s/.test(element)))\n\t\t\t) {\n\t\t\t\tthrow new OmpTypeError(\"A postfix required element cannot follow an optional or defaultable element\");\n\t\t\t}\n\t\t}\n\t\tconst parameterDefinitions = (marker === -1 ? definitions : definitions.slice(0, marker)).map(\n\t\t\tnormalizeFnParameter,\n\t\t);\n\t\tconst params = makeType<readonly unknown[], readonly unknown[]>(parseDef(parameterDefinitions, resolve), [], {});\n\t\tconst returns =\n\t\t\tmarker === -1\n\t\t\t\t? makeType<unknown>({ k: \"unknown\" }, [], {})\n\t\t\t\t: makeType<unknown>(parseDef(definitions[marker + 1], resolve), [], {});\n\t\tconst parameterExpression = fnExpression(params.ir);\n\t\tconst returnsExpression = fnExpression(returns.ir);\n\t\treturn (implementation: (...arguments_: readonly unknown[]) => unknown) => {\n\t\t\tif (typeof implementation !== \"function\") throw new OmpTypeError(\"type.fn requires a function implementation\");\n\t\t\tconst raw = (...arguments_: readonly unknown[]): unknown => {\n\t\t\t\tconst validatedArguments = params.assert(arguments_);\n\t\t\t\tconst result = Reflect.apply(implementation, undefined, validatedArguments);\n\t\t\t\treturn returns.assert(result);","sourceCodeStart":3103,"sourceCodeEnd":3139,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/type.ts#L3103-L3139","documentation":"When a tuple/function definition contains exactly one spread element followed by a required element, omptype requires that every element preceding the spread be required as well. If any element before the spread is optional (ends with `?`) or has a default (`= value`), the trailing required element could never be reliably positioned, so the library throws instead of building an ambiguous type.","triggerScenarios":"Calling `type([...])` (or `type.fn`) with a tuple-style definition containing one spread (`...`) element, a required element after the spread, and at least one optional (`\"x?\"`) or defaultable (\"x = 1\") element before the spread.","commonSituations":"Writing variadic tuple schemas like `[\"number?\", \"...string[]\", \"boolean\"]`; often the author intended the last element to also be optional, or copied a TypeScript pattern TypeScript itself would also reject.","solutions":["Make the postfix element optional (append `?`) so nothing required follows the spread.","Remove the optional/default marker from elements preceding the spread.","Move the spread so no required element follows it, or split the definition into multiple types."],"exampleFix":"// before\ntype([\"number?\", \"...string[]\", \"boolean\"])\n// after\ntype([\"number?\", \"...string[]\", \"boolean?\"])","handlingStrategy":"try-catch","validationCode":"function hasOptionalBeforeSpread(defs) { const i = defs.findIndex(d => typeof d === 'string' && d.startsWith('...')); const after = defs.slice(i + 1); return !(i !== -1 && after.length > 0 && defs.slice(0, i).some(d => typeof d === 'string' && (d.endsWith('?') || /\\s=\\s/.test(d)))); }","typeGuard":null,"tryCatchPattern":"try { const t = type(['number?', '...string[]', 'boolean?']); } catch (e) { if (e instanceof OmpTypeError) fixDefinition(); else throw e; }","preventionTips":["Keep required elements only before spreads in tuple definitions","Make every element after a spread optional or absorb it into the spread","Port tuple definitions from TypeScript carefully — TS also forbids required elements after optional rest patterns"],"tags":["schema","tuple","spread","validation"],"backgroundTag":"invalid-tuple-spread-order","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}