英文:
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 '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"
}
}
}
Here's the hook usage:
const { github, edit } = useI18n('postnav')
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'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<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
}
See it in action in TypeScript Playground.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论