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

如何基于属性存在判别TypeScript类型?无需强制使用type字段

问题:TypeScript/JavaScript作者列表建模困境

我是TypeScript(及JavaScript)新手,在建模作者列表时遇到以下需求:

  • 作者对象要么包含个人姓名属性(family/given),要么仅包含name属性(用于机构类主体)
  • 允许可选的location属性(针对出版商、机构类作者)
  • 能通过typescript-json-schema自动导出JSON Schema,加载JSON后可正确识别类型以调用关联方法

目前我通过class-transformer的判别器实现:

@Type(() => Agent, {
  discriminator: {
    property: 'type',
    subTypes: [
      { value: Organization, name: 'organization' },
      { value: Person, name: 'person' },
    ],
  },
})
author?: (Organization|Person)[];

该方案能正常运行,示例结果:

Reference {
  id: "un",
  type: "article",
  author: [ Organization { name: "United Nations" } ],
  title: "The Title",
  issued: "2020"
}

但局限性明显:必须在JSON输入中添加type字段用于类型判别,而我希望通过是否存在family/given属性自动判断类型,无需额外的type字段。请问有更优替代方案吗?


替代方案:基于属性存在性的自定义类型转换

可以通过class-transformer的Transform装饰器结合自定义逻辑,实现无需type字段的类型判别,同时兼容typescript-json-schema的导出需求。

1. 定义基础类与子类

先明确Person、Organization和基础Agent类的结构,结合class-validator做基础校验:

import { Transform, plainToInstance } from 'class-transformer';
import { IsOptional, IsString, ValidateNested } from 'class-validator';

// 基础Agent类,包含通用属性
class Agent {
  @IsOptional()
  @IsString()
  location?: string;
}

// 个人作者类,继承Agent并添加姓名属性
class Person extends Agent {
  @IsString()
  family: string;

  @IsString()
  given: string;
}

// 机构作者类,继承Agent并添加名称属性
class Organization extends Agent {
  @IsString()
  name: string;
}

2. 实现自定义类型转换逻辑

在Reference类的author字段上,用Transform装饰器替代原有判别器,根据属性存在性自动判断并转换类型:

class Reference {
  @IsString()
  id: string;

  @IsString()
  type: string;

  @IsOptional()
  @ValidateNested({ each: true })
  @Transform(({ value }) => {
    if (!value) return value;
    // 处理单作者或多作者列表
    const items = Array.isArray(value) ? value : [value];
    
    return items.map(item => {
      // 存在family和given字段则转为Person实例
      if ('family' in item && 'given' in item) {
        return plainToInstance(Person, item);
      }
      // 存在name字段则转为Organization实例
      else if ('name' in item) {
        return plainToInstance(Organization, item);
      }
      throw new Error(`Invalid author structure: ${JSON.stringify(item)}`);
    });
  }, { toClassOnly: true })
  author?: (Person | Organization)[];

  @IsString()
  title: string;

  @IsString()
  issued: string;
}

3. 兼容JSON Schema导出

typescript-json-schema会自动识别Person | Organization联合类型,生成包含oneOf规则的JSON Schema,符合你的导出需求。只需确保字段上显式声明联合类型即可,无需额外配置。

验证效果

输入不含type字段的JSON数据:

{
  "id": "un",
  "type": "article",
  "author": [
    { "name": "United Nations", "location": "New York" },
    { "family": "Doe", "given": "John" }
  ],
  "title": "The Title",
  "issued": "2020"
}

通过plainToInstance(Reference, jsonData)转换后,会得到正确的实例:

Reference {
  id: "un",
  type: "article",
  author: [
    Organization { name: "United Nations", location: "New York" },
    Person { family: "Doe", given: "John", location: undefined }
  ],
  title: "The Title",
  issued: "2020"
}

注意事项

  • 确保输入数据结构唯一:不要同时给一个作者对象设置name和family/given字段,否则会优先匹配Person类型
  • 若需要更严谨的校验,可以添加自定义验证器,在转换前校验每个作者对象的结构合法性
  • 如果使用class-validator做全局校验,记得开启ValidateNested确保子类属性也被校验

内容的提问来源于stack exchange,提问作者Bruce D'Arcus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:07:51