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

TypeScript中如何定义必须包含值为'1'项的Map类型?

如何定义强制包含值为1的SupportedUnits类型?

现有TypeScript代码如下:

type SupportedUnits = Map<string, BigNumber>;

class CurrencyTools{
private supportedUnits: SupportedUnits;
  /**
   * @constructor
   * @param supportedUnits - A map in which the keys are the unit names and the values are their ratio in relation to the main unit.
   */

  constructor(supportedUnits: SupportedUnits) {
    this.supportedUnits = supportedUnits;
  }

    getMainUnit(): string {
    const denomination = Array.from(this.supportedUnits.keys()).find(
      (key) => this.supportedUnits.get(key)?.toString() === '1'
    );

    return denomination || '';
  }
}

能否定义SupportedUnits类型,使其强制要求映射中存在值为1的项,从而让getMainUnit方法可直接返回denomination(无需使用denomination || '')?即类型层面确保映射包含值为1的项,同时允许任意键名。

可以通过类型约束结合运行时校验实现需求,下面提供两种可行方案:

方案一:基于Map的类型约束+运行时断言

保留Map原生特性,通过辅助函数确保输入符合要求:

import { BigNumber } from "bignumber.js";

// 定义SupportedUnits类型,约束为包含值为1的项的Map
type SupportedUnits = Map<string, BigNumber>;

// 辅助校验函数,确保传入的Map存在值为1的项
function validateSupportedUnits(units: Map<string, BigNumber>): SupportedUnits {
  const hasValidMainUnit = Array.from(units.values()).some(val => val.eq(1));
  if (!hasValidMainUnit) {
    throw new Error("SupportedUnits必须包含值为1的单位");
  }
  return units;
}

class CurrencyTools {
  private supportedUnits: SupportedUnits;

  constructor(supportedUnits: SupportedUnits) {
    // 构造函数内二次校验,避免非法输入
    this.supportedUnits = validateSupportedUnits(supportedUnits);
  }

  getMainUnit(): string {
    // 类型和运行时双重保障,用非空断言消除undefined
    const denomination = Array.from(this.supportedUnits.keys()).find(
      (key) => this.supportedUnits.get(key)!.eq(1)
    )!;

    return denomination;
  }
}

// 使用示例
const validUnits = new Map([
  ["BTC", new BigNumber(1)],
  ["mBTC", new BigNumber(0.001)],
  ["sat", new BigNumber(1e-8)]
]);
const tools = new CurrencyTools(validUnits);
console.log(tools.getMainUnit()); // 输出"BTC"

方案二:泛型+Record的静态类型约束

通过泛型在编译期就严格约束主单位的存在:

import { BigNumber } from "bignumber.js";

// 泛型类型,T为主单位键名,U为其他可选单位键名
type SupportedUnits<T extends string, U extends string = never> = 
  Record<T, BigNumber> & Partial<Record<U, BigNumber>> & {
    // 模拟Map核心方法,保持用法一致
    [Symbol.iterator]: () => IterableIterator<[string, BigNumber]>;
    get: (key: string) => BigNumber | undefined;
    has: (key: string) => boolean;
  };

// 辅助创建函数,自动设置主单位的值为1
function createSupportedUnits<T extends string>(mainUnit: T, otherUnits?: Record<string, BigNumber>): SupportedUnits<T> {
  const unitMap = new Map<string, BigNumber>();
  unitMap.set(mainUnit, new BigNumber(1));
  if (otherUnits) {
    Object.entries(otherUnits).forEach(([key, val]) => unitMap.set(key, val));
  }
  return unitMap as unknown as SupportedUnits<T>;
}

class CurrencyTools<T extends string> {
  private supportedUnits: SupportedUnits<T>;

  constructor(supportedUnits: SupportedUnits<T>) {
    this.supportedUnits = supportedUnits;
  }

  getMainUnit(): T {
    // 泛型已确保主单位存在,直接查找返回
    return Object.keys(this.supportedUnits).find(key => this.supportedUnits[key].eq(1)) as T;
  }
}

// 使用示例
const units = createSupportedUnits("BTC", {
  mBTC: new BigNumber(0.001),
  sat: new BigNumber(1e-8)
});
const tools = new CurrencyTools(units);
console.log(tools.getMainUnit()); // 类型为"BTC",输出"BTC"

核心要点

  • 双重保障:方案一通过运行时校验+非空断言消除兜底逻辑;方案二利用泛型在编译期锁定主单位存在。
  • 灵活性保留:两种方案都允许添加任意键名的单位,满足需求中的灵活性要求。
  • 可靠性提升:运行时校验避免了绕过TypeScript类型检查的非法输入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 11:27:56