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'.
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
- Found multiple decorators, skipping..
- Found non-mobx decorator @
- Please use `@observable accessor
AI-assisted analysis of mobxjs/mobx@01211a698b (2026-08-28).
Data as JSON: /api/errors/6f447967851d8224.
Report an issue: GitHub.
Appendix: 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)