mobxjs/mobx · error
Unexpected type
Error message
Unexpected type
What it means
In the codemod that rewrites legacy decorators to makeObservable calls, when a class has private members the generated initializeObservables call needs the class name as a type argument. If the class node has no simple identifier name (e.g. an anonymous or complex class expression), the transform cannot produce the type reference and throws 'Unexpected type'.
Source
Thrown at packages/mobx-undecorate/src/undecorate.ts:442
])
return property
}
function createConstructor(
clazz: ClassDeclaration,
members: ObjectExpression,
privates: string[]
) {
// makeObservable(this, { members })
const initializeObservablesCall = j.expressionStatement(
j.callExpression(
j.identifier("makeObservable"),
options?.keepDecorators ? [j.thisExpression()] : [j.thisExpression(), members]
)
)
if (privates.length && !options?.keepDecorators) {
if (typeof clazz.id!.name !== "string") {
throw new Error("Unexpected type")
}
// @ts-ignore
initializeObservablesCall.expression.typeArguments = j.tsTypeParameterInstantiation([
j.tsTypeReference(j.identifier(clazz.id!.name)),
j.tsUnionType(
// @ts-ignore
privates.map(member => j.tsLiteralType(j.stringLiteral(member)))
)
])
}
const needsSuper = !!clazz.superClass
let constructorIndex = clazz.body.body.findIndex(
member => j.ClassMethod.check(member) && member.kind === "constructor"
)
// create a constructor
if (constructorIndex === -1) {View on GitHub (pinned to 01211a698b)
Solutions
- Give the class an explicit name before running the transform (e.g. assign the class expression to a named variable or declare a named class)
- Use `npx mobx-undecorate --keepDecorators` so the private-member makeObservable path with type arguments is skipped
- Manually rewrite the decorators to makeObservable for the offending class and exclude it from the codemod
Example fix
// before
export default class {
@observable private x = 1
}
// after
class MyStore {
@observable private x = 1
}
export default MyStore Defensive patterns
Strategy: validation
Validate before calling
// before running the codemod, ensure classes are named
const files = glob('src/**/*.ts(x)')
for (const f of files) {
if (/class\s*(\{|extends)/.test(read(f))) console.warn(`${f}: anonymous class found — name it before undecorate`)
} Type guard
function isNamedClass(node: any): boolean {
return node?.id?.type === 'Identifier' && typeof node.id.name === 'string'
} Try / catch
try {
runCodemod(files)
} catch (e) {
if (e.message === 'Unexpected type') {
console.error('Codemod hit an unnamed class; name all classes with private members and rerun')
} else throw e
} Prevention
- Avoid anonymous class expressions in decorator-heavy code
- Pre-declare class names before running mobx-undecorate
- Run the codemod on one directory at a time to isolate failures
- Use --keepDecorators when private members are present
When it happens
Trigger: Running the mobx-undecorate transform on a source file containing a class with private fields and a class node whose `id` is not a named identifier (anonymous class expression or otherwise unnamed class).
Common situations: Running npx mobx-undecorate --keepDecorators=false over codebases containing default-exported anonymous classes or class expressions assigned to variables; generated/minified input class definitions.
Related errors
- Expected a decorate call with two arguments
- Expected exactly one class declaration for '${target.name}'
- Found multiple decorators, skipping..
- Found non-mobx decorator @${expr.name}
- Static properties are not supported ${property.key.loc?.sour
AI-assisted analysis of mobxjs/mobx@01211a698b (2026-08-28).
Data as JSON: /api/errors/6f447967851d8224.
Report an issue: GitHub.