typescript hur man läser json-data 2 nivåer djupt utan eslint-varningar


Exempel JSON

{
  "files": {
    "group1": {
      "subgroup1": ["file1", "file2"]
    },
    "group2": {
      "subgroup2": ["file3", "file4"],
      "subgroup3": ["file5"]
    }
  }
}

Jag försöker bygga en funktion som ger mig en slumpmässig fil, givet grupp- och undergruppsnamn

Försök 1

import data from "./data.json";

function getRandomImage(group: string, subgroup: string) {
  const files = data.files[group][subgroup];
  return files[Math.floor(Math.random() * files.length)];
}

Koden ovan ger mig detta fel

Elementet har implicit typen 'any' eftersom ett uttryck av typen 'string' inte kan användas för att indexera typen '{ group1: { subgroup1: string[]; }; group2: { subgroup2: string[]; subgroup3: string[]; }; }'. Ingen indexsignatur med en parameter av typen 'string' hittades på typen '{ group1: { subgroup1: string[]; }; group2: { subgroup2: string[]; subgroup3: string[]; }; }'.

Försök 2

function getRandomImage(data: any, group: string, subgroup: string) {
  const files = data.files[group][subgroup];
  return files[Math.floor(Math.random() * files.length)];
}

jag försökte också skicka data med vilken typ som helst, men det ger ett fel

Oväntad any. Specificera en annan typ.

Försök 3

type groupType = keyof typeof data.files;
type subGroupType = keyof typeof data.files[groupType];

function getRandomImage(group: groupType, subgroup: subGroupType) {
  const files = data.files[group][subgroup];
  return files[Math.floor(Math.random() * files.length)];
}

det ger mig ett fel

Egenskapen 'length' finns inte på typen 'never'.

Jag är väldigt ny på TypeScript (kommer från Python), så det här har varit ett stort besvär.. snälla hjälp mig.

Svar #1

Om du vill ha en typad funktion för manuell användning (dvs. den föreslår tillgängliga argument), härled möjligen det andra argumentet från det första argumentet:

// imported json is const type
const json = {
  "files": {
    "group1": {
      "subgroup1": ["file1", "file2"]
    },
    "group2": {
      "subgroup2": ["file3", "file4"],
      "subgroup3": ["file5"]
    }
  }
} as const;
type jsonT = typeof json.files;

function getRandomImage<
  K1 extends keyof jsonT,
  K2 extends keyof jsonT[K1]
>(group: K1, subgroup: K2) {
  const files = json.files[group][subgroup] as string[];
  return files[Math.floor(Math.random() * files.length)];
}
// won't allow improper args
getRandomImage('group1', 'subgroup1')

Om du inte bryr dig om att nycklarna är riktiga nycklar, vidga typerna till sträng:

// widen the type to {"a":{"b":["c"]}} with any strings
const filesRecord: Record<string, Record<string, string[]>> = json.files;
// or {[p: string]: {[p2: string]: string[]}} which means same


function getRandomImage(group: string, subgroup: string) {
  const files = data.files[group][subgroup];
  return files[Math.floor(Math.random() * files.length)];
}