Typescript在使用带有类型的JSON对象时会抛出警告。

huangapple go评论68阅读模式
英文:

Typescript throws warning when using JSON objects with types

问题

我正在尝试在hook内获取此JSON对象的属性,使用方括号表示法。我确信这在工作,但TypeScript一直抛出警告:

> 属性'github'在类型'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
}


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')


我期望得到正确的类型并轻松扩展。
英文:

I'm trying to get the properties of this JSON object inside a hook, using bracket notation. I'm sure this is working but Typescript keeps throwing that warning:

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

useI18n.ts

import { useRouter } from &#39;next/router&#39;
import i18n from &#39;i18n/index.json&#39;

type JSONRaw = typeof i18n
type Name = keyof JSONRaw
type Language = keyof JSONRaw[Name]
type Selection = JSONRaw[Name]
type Property = Pick&lt;Selection, Language&gt;
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
}

JSON

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

Here's the hook usage:

const { github, edit } = useI18n(&#39;postnav&#39;)

I expect to get the right types and scale it easy

答案1

得分: 1

你需要使用通用参数,以便TS可以在调用时推断出`name`的实际字符串字面值。

根据我的实验,你还需要手动添加返回类型注释来引导TS引擎,否则它会将返回类型推断为`{ text: string } | { github: string; edit: string }`,这是正确的,但不够具体。

综合起来,结果如下:

```ts
function useI18n<N extends Name>(name: N): JSONRaw[N][Language] {
  const { locale } = useRouter()
  const JSONFile: JSONRaw = i18n
  const wantedTextList = JSONFile[name]
  const localeLanguage = locale as Language
  const value = wantedTextList[localeLanguage]
  return value
}

TypeScript Playground中查看它的运行效果。


<details>
<summary>英文:</summary>

You need to use generic parameter so that TS can infer the actual string literal value for `name` at the calling site.

And from my experiment, you also need to manually add a return type annotation to guide TS engine, otherwise it&#39;ll just infer the return type to be `{ text: string } | { github: string; edit: string }` which is correct but not specific enough.

Put together, the result is:


```ts
function useI18n&lt;N extends Name&gt;(name: N): JSONRaw[N][Language] {
  const { locale } = useRouter()
  const JSONFile: JSONRaw = i18n
  const wantedTextList = JSONFile[name]
  const localeLanguage = locale as Language
  const value = wantedTextList[localeLanguage]
  return value
}

See it in action in TypeScript Playground.

huangapple
  • 本文由 发表于 2023年2月8日 10:00:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75380715.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定