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

如何在TypeScript中实现类似JS的多类型参数映射式处理?

问题

我有一个可接收多种类型参数的函数(具体是Proxy的get处理器),需要根据参数类型执行差异化处理。在原生JavaScript中,可以通过构造函数名称映射对应处理函数实现,代码如下:

const calls = {
    "String": (str)=>{/*...*/},
    "Number": (num)=>{/*...*/},
    "Symbol": (symb)=>{/*...*/},
    "Object": (obj)=>{/*...*/}
}
function foo(obj){
    const name = Object.getPrototypeOf(obj).constructor.name;
    if (name in calls) {
        calls[name](obj);
    } else {
    /*未知类型处理*/
    }
}

但在TypeScript中,为了保留类型追踪,目前只能想到链式类型守卫的方案,代码如下:

// isNumber, isString, isSymbol, isObject - 类型守卫(type guard)
function foo(obj: string | number | symbol):void {
    if (isString(obj)){
    /*...*/
    } else if (isNumber(obj)) {
    /*...*/
    } else if (isSymbol(obj)) {
    /*...*/
    } else if (isObject(obj)) {
    /*...*/
    } else {
    /*未知类型处理*/
    }
}

这个方案相比JS实现显得冗余,类型增多时会逐渐失控,还存在不必要的分支判断。想询问:TypeScript中是否存在与该JS方案等价的实现方式?

我还提供了更具体的场景示例:

class Container {
    constructor(){}
    metaData = {};
    props = new Map();
}

const valuePreProcess = {
    "Number"(target, prop, value){ return value.toString() },
    "Container"(target, prop, value){ value.metaData.parent = target; return value; }
}

const handlers = {
    set(target, prop, value){
        // 需要确定value的类型,才能正确调用对应的处理函数
        const processedValue = valuePreProcess[<typeOfValue>](target, prop, value);
        target.props.set(prop, processedValue);
    }
}

const obj = new Proxy(new Container(),handlers);

还有一种使用原型作为键的映射思路:

const valuePreProcessors = new Map([
    [Number.prototype,(target, prop, value)=>value.toString()],
    [Container.prototype,(target, prop, value)=>{ value.metaData.parent = target; return value; }]
]);

const handlers = {
    set(target, prop, value){
        const processFunc = valuePreProcessors.get(Object.getPrototypeOf(value));
        target.props.set(prop, processFunc(target, prop, value));
    }
}

解决方案

在TypeScript中,完全可以实现和JS等价的映射式类型处理,同时保留完整的类型追踪。下面是几种可行的方案:

1. 基于构造函数名称的类型安全映射

通过定义类型到处理函数的映射类型,结合类型断言和索引访问,实现类型安全的调用:

// 先定义支持的类型集合
type SupportedType = string | number | symbol | Container;

// 定义处理函数的类型签名,根据输入类型返回对应结果
type ProcessorMap = {
  String: (target: Container, prop: PropertyKey, value: string) => string;
  Number: (target: Container, prop: PropertyKey, value: number) => string;
  Symbol: (target: Container, prop: PropertyKey, value: symbol) => symbol;
  Container: (target: Container, prop: PropertyKey, value: Container) => Container;
};

const valuePreProcess: ProcessorMap = {
  String: (target, prop, value) => value.toUpperCase(), // 示例处理逻辑
  Number: (target, prop, value) => value.toString(16),
  Symbol: (target, prop, value) => Symbol.for(value.description || ""),
  Container: (target, prop, value) => {
    value.metaData.parent = target;
    return value;
  },
};

class Container {
  metaData = {};
  props = new Map<PropertyKey, unknown>();
}

const handlers = {
  set(target: Container, prop: PropertyKey, value: SupportedType): boolean {
    // 获取构造函数名称,并断言为ProcessorMap的键类型
    const constructorName = (Object.getPrototypeOf(value).constructor.name as keyof ProcessorMap);
    const processor = valuePreProcess[constructorName];
    
    if (processor) {
      const processedValue = processor(target, prop, value as never);
      target.props.set(prop, processedValue);
      return true;
    }
    
    // 未知类型处理
    target.props.set(prop, value);
    return true;
  },
};

const obj = new Proxy(new Container(), handlers);

这种方式和你原来的JS逻辑几乎一致,同时通过ProcessorMap和SupportedType约束了类型,确保处理函数的输入输出类型正确。

2. 基于原型的Map方案(增强类型安全)

你提到的原型作为键的思路可以优化为类型安全版本,通过定义Map的键值类型,避免不必要的类型断言:

class Container {
  metaData = {};
  props = new Map<PropertyKey, unknown>();
}

// 定义处理器的类型签名
type ValueProcessor = (target: Container, prop: PropertyKey, value: any) => unknown;

// 定义Map的键为构造函数原型,值为对应处理器
const valuePreProcessors = new Map<object, ValueProcessor>([
  [String.prototype, (target, prop, value: string) => value.toLowerCase()],
  [Number.prototype, (target, prop, value: number) => value.toFixed(2)],
  [Container.prototype, (target, prop, value: Container) => {
    value.metaData.parent = target;
    return value;
  }],
]);

const handlers = {
  set(target: Container, prop: PropertyKey, value: unknown): boolean {
    const proto = Object.getPrototypeOf(value);
    const processor = valuePreProcessors.get(proto);
    
    if (processor) {
      target.props.set(prop, processor(target, prop, value));
      return true;
    }
    
    // 未知类型处理
    target.props.set(prop, value);
    return true;
  },
};

const obj = new Proxy(new Container(), handlers);

这里通过Map<object, ValueProcessor>明确了键的类型为原型对象,同时在处理器内部可以针对具体类型做类型约束,保证类型安全。

3. 利用类型谓词自动推断类型

如果需要更严格的类型推断,可以结合类型谓词和映射,让TypeScript自动推导处理函数的参数类型:

class Container {
  metaData = {};
  props = new Map<PropertyKey, unknown>();
}

// 类型谓词集合
function isString(value: unknown): value is string {
  return typeof value === "string";
}
function isNumber(value: unknown): value is number {
  return typeof value === "number";
}
function isContainer(value: unknown): value is Container {
  return value instanceof Container;
}

// 定义类型到处理器的映射,同时关联类型谓词
const typeProcessors = [
  { predicate: isString, handler: (target: Container, prop: PropertyKey, value: string) => value.toUpperCase() },
  { predicate: isNumber, handler: (target: Container, prop: PropertyKey, value: number) => value.toString() },
  { predicate: isContainer, handler: (target: Container, prop: PropertyKey, value: Container) => {
    value.metaData.parent = target;
    return value;
  } },
];

const handlers = {
  set(target: Container, prop: PropertyKey, value: unknown): boolean {
    const processor = typeProcessors.find(item => item.predicate(value));
    if (processor) {
      target.props.set(prop, processor.handler(target, prop, value as never));
      return true;
    }
    
    target.props.set(prop, value);
    return true;
  },
};

const obj = new Proxy(new Container(), handlers);

这种方式兼顾了映射的简洁性和类型守卫的严格性,类型增多时只需要添加新的{ predicate, handler }条目即可,比链式else if更易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 08:10:28