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

TypeScript为反序列化JSON对象添加方法的最佳实践及Scala对比

原始数据与需求

原始JSON数据

[
    {
        "Value": "100000000",
        "Duration": 1
    },
    {
        "Value": "100000001",
        "Duration": 2
    },
    {
        "Value": "100000002",
        "Duration": 3
    },
    {
        "Value": "100000003",
        "Duration": 5
    },
    {
        "Value": "100000004",
        "Duration": 0
    },
    {
        "Value": "100000005",
        "Duration": 8
    },
    {
        "Value": "100000006",
        "Duration": 10
    }
]

初始接口定义

interface Duration {
  value: string
  duration: number
}

核心需求

为Duration接口添加可在所有对应对象上调用的方法:

durationInSeconds(): number {
  return this.duration * 1000
}

目标使用场景

const all = previousJsonContent
const durations: Duration[] = parseAsObjects(all) // 实际为JSON.parse(all)
const firstInSeconds = durations[0].durationInSeconds()

TypeScript方案

惯用实现与低侵入性方案

1. 类+工厂函数(推荐,类型安全+低侵入)

JSON反序列化得到的是普通对象,直接修改全局原型会造成污染,更合理的方式是定义实现Duration接口的类,通过工厂函数完成普通对象到类实例的转换:

interface Duration {
  value: string;
  duration: number;
}

class DurationImpl implements Duration {
  value: string;
  duration: number;

  constructor(data: Duration) {
    Object.assign(this, data);
  }

  durationInSeconds(): number {
    return this.duration * 1000;
  }
}

// 适配JSON键名的转换函数
function parseAsObjects(jsonStr: string): DurationImpl[] {
  return JSON.parse(jsonStr).map((item: any) => new DurationImpl({
    value: item.Value,
    duration: item.Duration
  }));
}

// 使用示例
const all = '[{"Value":"100000000","Duration":1},...]';
const durations = parseAsObjects(all);
console.log(durations[0].durationInSeconds()); // 输出 1000

该方案完全不污染全局,类型校验严格,且支持后续扩展深层字段的方法。

2. 模块级工具函数(极低侵入)

如果不需要对象方法的调用形式,直接写工具函数是侵入性最低的方案:

interface Duration {
  value: string;
  duration: number;
}

function durationInSeconds(dur: Duration): number {
  return dur.duration * 1000;
}

// 使用示例
const durations: Duration[] = JSON.parse(all).map(item => ({
  value: item.Value,
  duration: item.Duration
}));
console.log(durationInSeconds(durations[0])); // 输出 1000

3. 原型扩展(不推荐)

若坚持要对象方法形式且接受全局污染,可扩展Object原型,但容易引发命名冲突:

interface Duration {
  value: string;
  duration: number;
  durationInSeconds(): number;
}

declare global {
  interface Object {
    durationInSeconds(): number;
  }
}

Object.prototype.durationInSeconds = function() {
  if ('duration' in this && typeof this.duration === 'number') {
    return this.duration * 1000;
  }
  throw new Error('当前对象不符合Duration结构');
};

// 使用示例
const durations: Duration[] = JSON.parse(all).map(item => ({
  value: item.Value,
  duration: item.Duration
}));
console.log(durations[0].durationInSeconds());

关于assign/prototype/mixins的选择

  • Object.assign适合在类构造中快速复制JSON数据到实例,是类方案的常用手段;
  • prototype扩展不推荐,会污染全局且类型不安全;
  • mixins适合多类共享方法,但单接口方法扩展用类方案更简洁。

Scala方案

值类+隐式包装(无性能损耗)

Scala中可通过值类(Value Class)包装数据避免额外对象开销,搭配隐式转换实现"富包装器"效果,让普通数据对象直接调用扩展方法。

1. 定义数据类与值类

// 对应JSON结构的普通数据类
case class DurationData(value: String, duration: Int)

// 值类:包装DurationData并添加扩展方法,继承AnyVal避免额外对象
case class RichDuration(d: DurationData) extends AnyVal {
  def durationInSeconds(): Long = d.duration * 1000L
}

// 隐式转换:自动将DurationData转为RichDuration
object DurationImplicits {
  implicit def toRichDuration(d: DurationData): RichDuration = RichDuration(d)
}

2. 反序列化与使用

结合JSON库(如Circe)完成反序列化,导入隐式转换后即可直接调用方法:

import DurationImplicits._
import io.circe.generic.auto._
import io.circe.parser.decode

// 原始JSON字符串
val jsonStr = """[{"Value":"100000000","Duration":1},...]"""
val parseResult = decode[List[DurationData]](jsonStr)

parseResult match {
  case Right(durations) =>
    println(durations.head.durationInSeconds()) // 输出 1000
  case Left(error) => println(s"JSON解析失败: $error")
}

说明

  • 值类继承AnyVal后会被编译优化,不会创建额外包装对象,无性能损耗;
  • 隐式转换无需手动包装对象,语法简洁自然;
  • 若需支持深层字段方法,只需给对应深层数据类定义类似的隐式值类即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 15:40:37