网站搜索

TypeScript 如何在不出现 ESLint 警告的情况下读取两层深的 JSON 数据


示例 JSON

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

我正在尝试创建一个函数,该函数可以根据组名和子组名给我一个随机文件

尝试 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)];
}

上述代码给我这个错误

元素隐式具有 'any' 类型,因为无法使用类型为 'string' 的表达式来索引类型 '{ group1: { subgroup1: string[]; }; group2: { subgroup2: string[]; subgroup3: string[]; }; }'。在类型 '{ group1: { subgroup1: string[]; }; group2: { subgroup2: string[]; subgroup3: string[]; }; }' 上未找到参数类型为 'string' 的索引签名。

尝试 2

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

我也尝试用任何类型传递数据,但那会报错

意外的 any 类型。请指定其他类型。

尝试 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)];
}

那给我错误

属性 'length' 在类型 'never' 上不存在。

我对 TypeScript 非常陌生(来自 Python),所以这一直很麻烦……请帮帮我。

答案#1

如果你想要用于手动使用的已键入函数(即它会建议可用的参数),请根据第一个参数推断可能的第二个参数:

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

如果你不在乎键是否是合格的键,将类型扩展为字符串:

// 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)];
}