You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中jackson-js多态类型拆分文件后反序列化Super表达式错误问题咨询

Jackson-js TypeScript Polymorphism: Fix "Super expression must either be null or a function" After Splitting Classes

I've run into this exact issue with jackson-js when splitting polymorphic classes into separate files, so let's break down what's happening and how to fix it.

The Problem Recap

When your Animal base class and Dog subclass are in the same file, jackson-js handles serialization/deserialization perfectly. But once you split them into separate files, calling objectMapper.parse() throws the error: "Super expression must either be null or a function". Even using import type { Dog } from "./Dog" doesn't fix it.

Why This Happens

The root cause is circular module references:

  • The Animal class's @JsonSubTypes decorator needs to reference Dog
  • The Dog class inherits from Animal

In ES module loading, this circular dependency means one of the classes won't be fully initialized when jackson-js tries to access it. When the decorator's () => Dog runs, it might get undefined instead of the actual class constructor. Using import type makes this worse because type-only imports are stripped at runtime—so Dog doesn't exist at all when jackson-js needs it.

Fixes That Work

Here are two reliable solutions to resolve this:

Solution 1: Use Dynamic require to Break the Cycle

Instead of importing Dog directly in Animal.ts, use Node.js's require() inside the @JsonSubTypes class factory function. This delays loading Dog until it's actually needed, avoiding the circular dependency during module initialization.

Update Animal.ts:

import { JsonClassType, JsonProperty, JsonTypeInfo, JsonTypeInfoId, JsonTypeInfoAs, JsonSubTypes } from "jackson-js";

@JsonTypeInfo({ use: JsonTypeInfoId.NAME, include: JsonTypeInfoAs.PROPERTY, property: "type" })
@JsonSubTypes({ 
  types: [
    { 
      class: () => require('./Dog').Dog, // Dynamically load Dog when needed
      name: "dog" 
    }
  ] 
})
export class Animal {
  @JsonProperty()
  @JsonClassType({ type: () => [String] })
  name: string;

  @JsonProperty()
  @JsonClassType({ type: () => [String] })
  type: string;

  constructor(type: string, name: string) {
    this.type = type;
    this.name = name;
  }

  say() {
    console.log("Ah!");
  }
}

Keep Dog.ts unchanged:

import { JsonTypeName } from "jackson-js";
import { Animal } from "./Animal";

@JsonTypeName({ value: "dog" })
export class Dog extends Animal {
  constructor(name: string) {
    super("dog", name);
  }

  say() {
    console.log("Wang!");
  }
}

Your test code stays the same—it'll work without any changes.

Solution 2: Manually Register Subclasses with ObjectMapper

Skip the @JsonSubTypes decorator on the base class, and instead register your subclasses directly with the ObjectMapper instance. This gives you explicit control over class loading order, eliminating the circular dependency issue.

  1. Update Animal.ts to remove @JsonSubTypes:
import { JsonClassType, JsonProperty, JsonTypeInfo, JsonTypeInfoId, JsonTypeInfoAs } from "jackson-js";

@JsonTypeInfo({ use: JsonTypeInfoId.NAME, include: JsonTypeInfoAs.PROPERTY, property: "type" })
export class Animal {
  @JsonProperty()
  @JsonClassType({ type: () => [String] })
  name: string;

  @JsonProperty()
  @JsonClassType({ type: () => [String] })
  type: string;

  constructor(type: string, name: string) {
    this.type = type;
    this.name = name;
  }

  say() {
    console.log("Ah!");
  }
}
  1. Keep Dog.ts unchanged.

  2. Modify your test code to register the subclass:

import { ObjectMapper } from "jackson-js";
import { Animal } from "./Animal";
import { Dog } from "./Dog";

const dog = new Dog("Super");
const objectMapper = new ObjectMapper();
// Register Dog with the ObjectMapper
objectMapper.registerSubtypes([{ class: Dog, name: "dog" }]);

const jsonString = objectMapper.stringify<Animal>(dog);
console.log(jsonString); // {"name":"Super","type":"dog"}
const animal = objectMapper.parse<Animal>(jsonString, { mainCreator: () => [Animal] });
animal.say(); // Wang!

Verify the Fix

Either solution will resolve the error. When you run your test code, you'll see the expected JSON output and "Wang!" logged to the console—confirming that polymorphic deserialization works correctly even with split files.

内容的提问来源于stack exchange,提问作者yejianfengblue

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:55:03