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

使用带类型的JSON对象时TypeScript抛出警告问题排查

问题

使用Hook通过方括号表示法获取JSON对象属性时功能正常,但TypeScript持续抛出警告:

Property 'github' does not exist on type 'PropertyValues'

相关代码与配置

useI18n.ts

import { useRouter } from "next/router"
import i18n from "i18n/index.json"

type JSONRaw = typeof i18n
type Name = keyof JSONRaw
type Language = keyof JSONRaw[Name]
type Selection = JSONRaw[Name]
type Property = Pick<Selection, Language>
type PropertyValues = Property[Language]

export default function useI18n (name: Name) {
  const { locale } = useRouter()

  const JSONFile: JSONRaw = i18n
  const wantedTextList = JSONFile[name]
  const localeLanguage = locale as Language
  const value = wantedTextList[localeLanguage]
  return value as PropertyValues
}

i18n/index.json

{
  "header": {
    "es-ar": {
      "text": "lorem ipsum 2."
    },
    "en-us": {
      "text": "lorem ipsum."
    }
  },
  "preferences": {
    "es-ar": {
      "text": "lorem ipsum 2"
    },
    "en-us": {
      "text": "lorem ipsum"
    }
  },
  
  "notavaible": {
    "es-ar": {
      "text": "lorem ipsum 2"
    },
    "en-us": {
      "text": "lorem ipsum"
    }
  },
  "postnav": {
    "en-us": {
      "github": "lorem ipsum 2",
      "edit": " lorem ipsum"
    },
    "es-ar": {
      "github": "lorem ipsum 2",
      "edit": "lorem ipsum"
    }
  }
}

Hook使用方式

const { github, edit } = useI18n('postnav')

需求:获得正确的类型支持,并实现轻松扩展。


解决方案

问题出在当前类型定义未根据传入的name参数动态推导返回值类型,PropertyValues被推断为所有语言属性值的交集,导致TypeScript无法识别特定模块的专属属性。

修改useI18n的类型定义,通过泛型让TypeScript根据传入的name精确推导返回类型:

import { useRouter } from "next/router"
import i18n from "i18n/index.json"

// 原始JSON结构类型
type JSONRaw = typeof i18n;
// 所有可用的模块名称
type ModuleName = keyof JSONRaw;
// 给定模块下的语言类型
type Language<M extends ModuleName> = keyof JSONRaw[M];
// 给定模块+语言对应的属性值类型
type ModuleValues<M extends ModuleName> = JSONRaw[M][Language<M>];

// 泛型函数:根据传入的模块名推导返回值类型
export default function useI18n<M extends ModuleName>(name: M): ModuleValues<M> {
  const { locale } = useRouter();

  const targetModule = i18n[name];
  // 断言locale属于当前模块的语言集合(需确保项目locale配置与JSON一致)
  const currentLocale = locale as Language<M>;
  
  return targetModule[currentLocale];
}

关键优化点

  • 泛型参数绑定:通过M extends ModuleName绑定传入的模块名,TypeScript能精准识别对应模块的属性结构。
  • 类型直接映射:ModuleValues<M>直接指向对应模块下当前语言的具体属性类型,比如传入'postnav'时,返回类型会自动包含github和edit属性。
  • 简化类型结构:移除冗余的中间类型定义,保留对JSON结构的强约束,后续扩展模块或属性时,类型会自动同步更新。

修改后,使用useI18n('postnav')解构github和edit时,TypeScript不再抛出警告,且后续扩展JSON内容无需手动修改类型定义,满足可扩展性需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:50:23