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

TypeScript使用keyof索引二维对象报TS2536错误的解决方法

问题场景

有存储多语言翻译映射的二维对象,结构示例:

const translations = {
    'login': {
        'e-mail': 'E-Mail',
        'username': 'Username',
        'forgot-password': 'Forgot your password?'
    },
    'dashboard': {
        'hello {x}': 'Hello {x} how is your day?',
        'go-to-other-page': 'Go to page...'
    }
}

预先定义AllTranslations接口声明所有翻译分类、下属文案的类型结构:

interface AllTranslations {
    login: {} // 存储login分类下所有文案
    dashboard: {} // 存储dashboard分类下所有文案
}

基于该接口实现translate翻译函数,根据传入的分类key、文案key、插值参数返回对应翻译文本:

type TranslationCategory<T = keyof AllTranslations> = keyof AllTranslations[T];

const translate = <T extends keyof AllTranslations>(key: T, message: TranslationCategory<T>, params: {[key: string]: string|number} = {} ): string => {
    const m = translations[key][message] ?? message;
    // 执行文案格式化、插值参数替换逻辑
    return m as string;
}

函数调用时分类、文案字段均可正常触发IDE自动补全:

translate('login', 'username');
translate('dashboard', 'hello {x}', {x: 'User'});

但TranslationCategory类型定义行抛出TS编译错误:TS2536: Type 'T' cannot be used to index type 'AllTranslations',需要在不使用//@ts-ignore忽略错误的前提下修复问题,保留完整的自动补全能力。

错误原因

类型别名TranslationCategory的泛型参数T只设置了默认值为keyof AllTranslations,但没有给泛型本身添加类型约束。TypeScript无法确定传入的T一定是AllTranslations的合法索引类型,因此不允许执行AllTranslations[T]索引操作,抛出编译错误。

修复方案

给TranslationCategory的泛型参数T添加extends keyof AllTranslations约束,明确T的类型范围一定是AllTranslations的合法key,即可消除编译错误,同时完全保留原有的类型校验、IDE自动补全能力。

修正后的完整代码如下:

interface AllTranslations {
    login: {} 
    dashboard: {} 
}

// 给泛型T添加类型约束,明确其一定是AllTranslations的合法key
type TranslationCategory<T extends keyof AllTranslations = keyof AllTranslations> = keyof AllTranslations[T];

const translate = <T extends keyof AllTranslations>(
  key: T, 
  message: TranslationCategory<T>, 
  params: {[key: string]: string|number} = {} 
): string => {
    const m = translations[key][message] ?? message;
    // 插值替换、格式化逻辑
    return Object.entries(params).reduce(
      (result, [paramKey, paramValue]) => result.replaceAll(`{${paramKey}}`, String(paramValue)),
      m as string
    );
}

可选优化

如果不想手动维护AllTranslations接口和实际translations对象的字段同步,可以直接用typeof从已定义的翻译常量对象推导类型,避免两边字段不一致导致的类型错误:

// 直接从translations常量推导全量翻译类型,无需手写接口
type AllTranslations = typeof translations;

type TranslationCategory<T extends keyof AllTranslations = keyof AllTranslations> = keyof AllTranslations[T];

const translate = <T extends keyof AllTranslations>(
  key: T, 
  message: TranslationCategory<T>, 
  params: {[key: string]: string|number} = {} 
): string => {
    const m = translations[key][message] ?? message;
    return Object.entries(params).reduce(
      (result, [paramKey, paramValue]) => result.replaceAll(`{${paramKey}}`, String(paramValue)),
      m
    );
}

该方案下,后续给translations对象新增分类、新增文案时,类型会自动同步,自动补全、类型校验会同步更新,无需手动修改接口定义。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 18:06:33