javascript - Typescript aruncă avertisment atunci când folosești obiecte JSON cu tipuri


Încerc să obțin proprietățile acestui obiect JSON într-un hook, folosind notația cu paranteze. Sunt sigur că funcționează, dar Typescript continuă să afișeze acel avertisment:

Proprietatea 'github' nu există pe tipul '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"
    }
  }
}

Iată utilizarea cârligului:

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

Mă aștept să obțin tipurile corecte și să le scalerez ușor

Răspuns #1

Trebuie să folosești un parametru generic astfel încât TS să poată deduce valoarea efectivă a literalului de șir pentru name la locul apelului.

Și din experimentul meu, trebuie de asemenea să adaugi manual o anotare a tipului de returnare pentru a ghida motorul TS, altfel acesta va deduce pur și simplu tipul de returnare ca fiind { text: string } | { github: string; edit: string }, ceea ce este corect dar nu suficient de specific.

Pus împreună, rezultatul este:

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
}

Vezi-l în acțiune în TypeScript Playground.