{"record":{"id":"f04709699f859292","repo":"can1357/oh-my-pi","slug":"duplicate-generic-parameter-parameter-name","errorCode":null,"errorMessage":"duplicate generic parameter \"${parameter.name}\"","messagePattern":"duplicate generic parameter \"(.+?)\"","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/type.ts","lineNumber":2511,"sourceCode":"}\n\n/** Schema arguments passed to a callback-bodied runtime generic. */\nexport interface GenericArguments {\n\treadonly [name: string]: BaseType;\n}\n\nexport interface GenericBuilder {\n\t(definition: (arguments_: GenericArguments) => unknown, hkt?: unknown): Generic;\n\t(definition: unknown, hkt?: unknown): Generic;\n}\n\nfunction validateGenericParameters(parameters: readonly GenericParameter[]): void {\n\tconst names = new Set<string>();\n\tfor (const parameter of parameters) {\n\t\tif (!/^[A-Za-z_$]\\w*$/.test(parameter.name)) {\n\t\t\tthrow new OmpTypeError(`invalid generic parameter \"${parameter.name}\"`);\n\t\t}\n\t\tif (names.has(parameter.name)) throw new OmpTypeError(`duplicate generic parameter \"${parameter.name}\"`);\n\t\tnames.add(parameter.name);\n\t}\n\tif (parameters.length === 0) throw new OmpTypeError(\"generic declarations require at least one parameter\");\n}\n\nfunction parseGenericParameters(source: string): GenericParameter[] {\n\tconst trimmed = source.trim();\n\tconst body = trimmed.startsWith(\"<\") && trimmed.endsWith(\">\") ? trimmed.slice(1, -1) : trimmed;\n\tconst parts: string[] = [];\n\tlet start = 0;\n\tlet depth = 0;\n\tlet quote = \"\";\n\tfor (let index = 0; index < body.length; index++) {\n\t\tconst char = body[index];\n\t\tif (quote !== \"\") {\n\t\t\tif (char === quote && body[index - 1] !== \"\\\\\") quote = \"\";\n\t\t\tcontinue;\n\t\t}","sourceCodeStart":2493,"sourceCodeEnd":2529,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/type.ts#L2493-L2529","documentation":"validateGenericParameters() rejects duplicate generic parameter names by tracking names in a Set and throwing OmpTypeError on a repeat. Duplicated placeholders are ambiguous — the body could not tell which argument to substitute where — so they are errors at declaration time.","triggerScenarios":"Declaring a generic with the same parameter twice, e.g. generic('<T, T>', ...) or generic('<K, V, K>', ...), including duplicates introduced by string splitting when the declaration is built dynamically.","commonSituations":"Copy/paste when extending a generic's parameter list; concatenating parameter strings from two sources that both use 'T'; typos where an intended distinct name was never changed.","solutions":["Rename one of the duplicated parameters to a distinct identifier","If merging parameter lists programmatically, deduplicate before joining (e.g. new Set(parts))","Review copy/pasted declarations for leftover repeated placeholders"],"exampleFix":"// before\nconst T = generic('<T, T>', 'record<T, T[]>'); // throws\n// after\nconst T = generic('<K, V>', 'record<K, V[]>');","handlingStrategy":"validation","validationCode":"function assertUniqueParams(names: readonly string[]) {\n  const dupes = names.filter((n, i) => names.indexOf(n) !== i);\n  if (dupes.length) throw new Error(`duplicate generic parameters: ${[...new Set(dupes)].join(', ')}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const G = generic(decl, body);\n} catch (err) {\n  if (err instanceof OmpTypeError && err.message.includes('duplicate generic parameter')) {\n    const name = err.message.match(/\"(.*)\"/)?.[1];\n    throw new Error(`Rename the repeated parameter \"${name}\" in \"${decl}\"`);\n  }\n  throw err;\n}","preventionTips":["Deduplicate merged parameter lists with new Set(parts) before joining","Use distinct names per role (K/V for key/value) to avoid collisions","Review copy/pasted declarations for leftovers"],"tags":["generics","schema","duplicate-names","omptype"],"backgroundTag":"invalid-generic-parameter","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}