Skip to content

TypeScript object mapping library for PO/DTO/VO transformations. Orika-JS 是一个专为 TypeScript 设计的对象映射库,用于在分层架构中优雅地处理 Entity、DTO、VO 之间的转换。

License

Notifications You must be signed in to change notification settings

stevenleep/orika-js

Orika-JS

类型安全的 TypeScript 对象映射库

npm version npm downloads License

English | 简体中文

简介

Orika-JS 是一个专为 TypeScript 设计的对象映射库,用于在分层架构中优雅地处理 Entity、DTO、VO 之间的转换。

核心特性

  • 类型安全 - 完整的 TypeScript 类型推导和编译时检查
  • 约定优于配置 - 同名字段自动映射,零配置即可使用
  • 高性能 - 智能缓存、批量处理、惰性求值
  • 异步支持 - 原生 Promise,支持异步字段转换
  • 框架集成 - 深度集成 React、Vue 3、Zustand、Jotai、Pinia
  • 轻量级 - 零运行时依赖,核心库仅 8KB gzipped

适用场景

  • API 层:后端 DTO 与前端 Model 转换
  • 业务层:Entity 与 DTO 之间的双向映射
  • 视图层:Model 转换为 ViewModel
  • 数据持久化:领域对象与数据库实体转换

安装

pnpm add @orika-js/core

框架集成包(可选):

pnpm add @orika-js/react      # React
pnpm add @orika-js/vue3       # Vue 3
pnpm add @orika-js/pinia      # Pinia
pnpm add @orika-js/zustand    # Zustand
pnpm add @orika-js/jotai      # Jotai

快速开始

基础映射

import { createMapperBuilder, MapperFactory } from '@orika-js/core';

// 定义数据模型
class User {
  id: number;
  username: string;
  password: string;
  email: string;
}

class UserDTO {
  id: number;
  displayName: string;
  email: string;
}

// 配置映射规则
createMapperBuilder<User, UserDTO>()
  .from(User)
  .to(UserDTO)
  .mapField('username', 'displayName')  // 字段重命名
  .exclude('password')                  // 排除敏感字段
  .register();

// 执行映射
const factory = MapperFactory.getInstance();

// 单个对象
const dto = factory.map(user, User, UserDTO);

// 批量映射
const dtos = factory.mapArray(users, User, UserDTO);

异步字段转换

适用于需要从其他数据源获取关联数据的场景。

createMapperBuilder<Post, PostDetailDTO>()
  .from(Post)
  .to(PostDetailDTO)
  .forMemberAsync('author', async (src) => {
    const user = await userService.getById(src.authorId);
    return factory.map(user, User, UserDTO);
  })
  .forMemberAsync('comments', async (src) => {
    return await commentService.getByPostId(src.id);
  })
  .register();

// 自动处理所有异步操作
const detail = await factory.mapAsync(post, Post, PostDetailDTO);

自定义转换逻辑

支持计算字段、条件映射等复杂场景。

createMapperBuilder<Product, ProductDTO>()
  .from(Product)
  .to(ProductDTO)
  // 计算字段
  .forMember('totalPrice', (src) => src.price * src.quantity)
  // 条件映射
  .mapFieldWhen('discountPrice', 'finalPrice',
    (src) => src.onSale,
    (src) => src.price * (1 - src.discount)
  )
  // 类型转换
  .forMember('createdAt', (src) => new Date(src.createTime))
  .register();

嵌套对象映射

处理复杂的对象结构。

createMapperBuilder<Order, OrderDTO>()
  .from(Order)
  .to(OrderDTO)
  .forMember('customer', (src) => 
    factory.map(src.user, User, UserDTO)
  )
  .forMember('items', (src) => 
    factory.mapArray(src.orderItems, OrderItem, OrderItemDTO)
  )
  .register();

使用场景

场景 1:API 响应转换

将后端 API 返回的数据转换为前端使用的模型。

// 后端返回的数据结构
interface APIResponse {
  user_id: number;
  user_name: string;
  created_at: string;
}

// 前端数据模型
class UserModel {
  id: number;
  name: string;
  createdAt: Date;
}

// 配置映射
createMapperBuilder<APIResponse, UserModel>()
  .from(Object as any)
  .to(UserModel)
  .mapField('user_id', 'id')
  .mapField('user_name', 'name')
  .forMember('createdAt', (src) => new Date(src.created_at))
  .register();

// 使用
const response = await api.getUser();
const user = factory.map(response, Object as any, UserModel);

场景 2:表单数据提交

将表单数据转换为 API 请求格式。

createMapperBuilder<FormData, CreateUserRequest>()
  .from(FormData)
  .to(CreateUserRequest)
  .forMember('age', (src) => parseInt(src.age))
  .forMember('tags', (src) => src.tagString.split(','))
  .validate((src, dest) => {
    if (!dest.email.includes('@')) {
      throw new Error('Invalid email format');
    }
  })
  .register();

场景 3:列表数据展示

为列表视图准备优化的数据结构。

createMapperBuilder<Product, ProductListItemDTO>()
  .from(Product)
  .to(ProductListItemDTO)
  .forMember('displayPrice', (src) => 
    src.onSale ? src.salePrice : src.originalPrice
  )
  .forMember('stockStatus', (src) => {
    if (src.stock === 0) return 'sold-out';
    if (src.stock < 10) return 'low-stock';
    return 'in-stock';
  })
  .register();

框架集成

React

import { useMapper, useMemoizedMapper } from '@orika-js/react';

function UserProfile({ user }) {
  // 自动响应依赖变化
  const dto = useMemoizedMapper(user, User, UserDTO);
  
  return <div>{dto.displayName}</div>;
}

完整文档:React 集成指南

Vue 3

import { useMapper, mapToReactive } from '@orika-js/vue3';

export default {
  setup() {
    const { map } = useMapper(User, UserDTO);
    
    // 响应式映射
    const userDTO = mapToReactive(user, User, UserDTO);
    
    return { userDTO };
  }
}

完整文档:Vue 3 集成指南

状态管理

// Pinia
import { createPiniaMapperPlugin } from '@orika-js/pinia';
pinia.use(createPiniaMapperPlugin());

// Zustand
import { createMappedStore } from '@orika-js/zustand';
const useStore = createMappedStore(/* ... */);

// Jotai
import { atomWithMapper } from '@orika-js/jotai';
const userDTOAtom = atomWithMapper(userAtom, User, UserDTO);

集成文档:PiniaZustandJotai

生态系统

版本 大小 描述
@orika-js/core npm 8KB 核心映射引擎
@orika-js/react npm 3KB React 集成
@orika-js/vue3 npm 3KB Vue 3 集成
@orika-js/pinia npm 2KB Pinia 插件
@orika-js/zustand npm 2KB Zustand 中间件
@orika-js/jotai npm 2KB Jotai 集成

文档与资源

开发

# 安装依赖
pnpm install

# 构建所有包
pnpm build

# 开发模式
pnpm dev

# 运行示例
pnpm --filter react-demo dev

贡献

欢迎贡献代码、报告问题或提出建议!请查看贡献指南了解详情。

许可证

MIT © Steven Lee


灵感来自 Orika (Java) 和 AutoMapper (.NET)

About

TypeScript object mapping library for PO/DTO/VO transformations. Orika-JS 是一个专为 TypeScript 设计的对象映射库,用于在分层架构中优雅地处理 Entity、DTO、VO 之间的转换。

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published