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

能否用TypeScript从字符串字典生成强类型Environment对象?

可行,且有成熟实现方案

核心思路是结合 TypeScript 的映射类型、字面量类型推断,以及 JavaScript 的 Proxy 特性,既实现动态 getter 的自动生成,又让 TypeScript 完整识别动态属性的类型,同时支持自定义 getter 的类型推导。

步骤1:定义类型转换工具

先写两个关键工具类型,分别处理键名的驼峰转换和值的类型解析:

// 把 REACT_APP_XXX_YYY 格式的键转为驼峰命名
type SnakeToCamel<S extends string> = 
  S extends `REACT_APP_${infer Rest}` 
    ? Rest extends `${infer First}_${infer Tail}`
      ? `${Lowercase<First>}${SnakeToCamel<Tail>}`
      : Lowercase<Rest>
    : S;

// 根据字符串字面量推断转换后的类型('true'→boolean、数字字符串→number,其余保留string)
type ParseValue<T extends string> = 
  T extends 'true' | 'false' ? boolean :
  T extends `${number}` ? number :
  T;

// 生成最终的代理类型:映射原变量的键为驼峰,值为解析后的类型
type ProxiedVariables<Var extends Record<string, string>> = {
  [K in keyof Var as SnakeToCamel<K>]: ParseValue<Var[K]>;
};

步骤2:用Proxy实现动态getter逻辑

通过 Proxy 拦截属性访问,自动处理前缀去除、驼峰转换和值类型转换,同时利用类型断言让 TypeScript 识别代理后的类型:

// 用as const保留字面量类型,确保类型推断精准
const variables = {
  REACT_APP_MY_VARIABLE: 'Test',
  REACT_APP_MY_BOOLEAN_VARIABLE: 'true',
  REACT_APP_MY_NUMBER_VARIABLE: '123'
} as const;

function createEnvProxy<Var extends Record<string, string>>(env: Var): ProxiedVariables<Var> {
  return new Proxy(env, {
    get(target, prop: string) {
      // 允许直接访问原始属性(如REACT_APP_MY_VARIABLE)
      if (prop in target) {
        return (target as any)[prop];
      }
      // 将驼峰属性转回带前缀的蛇形键
      const snakeProp = `REACT_APP_${prop.replace(/([A-Z])/g, '_$1').toUpperCase()}`;
      if (!(snakeProp in target)) {
        throw new Error(`环境变量 ${snakeProp} 不存在`);
      }
      // 执行值类型转换
      const rawValue = target[snakeProp];
      if (rawValue === 'true') return true;
      if (rawValue === 'false') return false;
      const num = Number(rawValue);
      if (!isNaN(num)) return num;
      return rawValue;
    }
  }) as ProxiedVariables<Var>;
}

// 使用代理对象,TypeScript会自动识别所有属性类型
const env = createEnvProxy(variables);
console.log(env.myVariable); // 类型:string,值:'Test'
console.log(env.myBooleanVariable); // 类型:boolean,值:true
console.log(env.myNumberVariable); // 类型:number,值:123

步骤3:扩展自定义getter

如果需要为特定属性添加自定义转换逻辑,可通过交叉类型扩展代理类型,并在 Proxy 中加入自定义处理:

// 假设REACT_APP_MY_SPECIAL_VARIABLE的值是"100,foo",需要转为对象
type CustomProxiedVariables = ProxiedVariables<typeof variables> & {
  mySpecialVariable: { id: number; name: string };
};

function createEnvProxyWithCustom<Var extends Record<string, string>>(env: Var): CustomProxiedVariables {
  const baseProxy = createEnvProxy(env);
  return new Proxy(baseProxy, {
    get(target, prop: string) {
      if (prop === 'mySpecialVariable') {
        const raw = target.mySpecialVariable;
        const [id, name] = raw.split(',');
        return { id: Number(id), name };
      }
      return (target as any)[prop];
    }
  }) as CustomProxiedVariables;
}

这种方案既避免了手动编写大量 getter,又让 TypeScript 完美识别所有动态属性和自定义 getter 的类型,完全满足需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 17:33:24