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

TypeScript中如何基于类的accessorName生成ServiceMap静态类型?

解决方案:用子类accessorName构建带静态类型的ServiceMap

要实现用子类的accessorName作为ServiceMap的键,同时保留完整的类型检查,需要调整类型定义和实例化逻辑,具体步骤如下:

1. 调整基础服务类(可选但推荐)

将基础Service类改为抽象类,强制子类必须实现accessorName静态属性,避免遗漏:

import { PrismaClient } from "@prisma/client";
// 提前声明ServiceMap类型,避免循环引用
export type ServiceMap = Record<string, Service>;

export default abstract class Service {
  public static abstract readonly accessorName: string;

  constructor(protected prisma: PrismaClient, protected services: ServiceMap) {}
}

2. 统一导出服务构造函数数组

创建一个包含所有服务类的常量数组,并使用as const让TypeScript保留每个类的具体类型(包括accessorName的字面量值):

// services/index.ts
import Users from "./Users";
import Warehouses from "./Warehouses";
import Depots from "./Depots";

// 用as const固定类型,让TS能捕获每个类的accessorName具体值
export const serviceConstructors = [Users, Warehouses, Depots] as const;

3. 构建正确的ServiceMap类型

通过映射类型遍历服务构造函数数组,提取每个类的accessorName作为键,实例类型作为值:

// services/index.ts
import type { Service } from "./Service";

// 提取构造函数数组的类型
type ServiceConstructorTuple = typeof serviceConstructors;

// 构建最终的ServiceMap类型:键是accessorName,值是对应类的实例
export type ServiceMap = {
  [K in ServiceConstructorTuple[number] as K["accessorName"]]: InstanceType<K>;
};

此时生成的ServiceMap类型完全符合需求:

type ServiceMap = {
  users: Users;
  warehouses: Warehouses;
  depots: Depots;
};

4. 实现类型安全的createServiceMap函数

修改实例化逻辑,逐个使用accessorName作为键填充map,同时解决类型断言问题:

// createServiceMap.ts
import { PrismaClient } from "@prisma/client";
import { serviceConstructors, ServiceMap } from "./services";

export default function createServiceMap(prisma: PrismaClient): ServiceMap {
  const map: Partial<ServiceMap> = {};

  // 遍历每个服务构造函数,用accessorName作为键创建实例
  for (const ServiceClass of serviceConstructors) {
    const key = ServiceClass.accessorName as keyof ServiceMap;
    // 断言map为ServiceMap,因为我们会逐步填充完整
    map[key] = new ServiceClass(prisma, map as ServiceMap);
  }

  return map as ServiceMap;
}

为什么之前的方案有问题?

  1. 键名不符:import * as services导出的键是变量名(类名Users/Warehouses),而非子类的accessorName属性值,所以生成的ServiceMap键是类名而非目标字符串。
  2. 类型错误:用typeof Service作为构造函数类型时,实例化结果被推断为基础Service类型,无法匹配ServiceMap中具体子类的实例类型;改用构造函数数组后,TypeScript能推断每个类的具体实例类型,避免类型不兼容问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 07:15:58