Динамические ключи объекта в typescript

Написал парсинг текстового файла на js:

function parseTextStream(textStream) {
        parsedData = textStream.split(lineSeparator).reduce(
          (dataAcc, line) => {
            let { reportPart, current, data } = dataAcc;
            reportPart = Object.keys(data).at(-1);
            const currentPart = data[reportPart];
            console.log(current, currentPart);
            for (const item of reportParts) {
              if (line.includes(item)) {
                reportPart = camelize(item);
                current = null;
                data[reportPart] =
                  reportPart === "terminalSettings" ||
                  reportPart === "devices"
                    ? []
                    : {};
                return { reportPart, current, data };
              }
            }

            return parseLine(line, data, reportPart, current);
          },
          { reportPart: null, current: null, data: { generalInfo: {} } }
        );

        // console.log(parsedData);
        return parsedData.data;
      }

      function parseLine(line, data, reportPart, current) {
        let reportPartObj = data[reportPart];
        if (entriesPattern.test(line)) {
          let entries = line.trim().split(entriesSeparator);
          let [key, value] = entries;
          key = camelize(key.trim());
          value = value.trim();
          if (
            value === "UNREGISTERED!" ||
            value === "empty" ||
            value === "set" ||
            value === "None"
          )
            value = null;
          if (ISODatePattern.test(value)) value = new Date(value);

          switch (reportPart) {
            case "licenseRelatedHardware":
              if (key === "FINGERPRINT" || key === "MACS") {
                reportPartObj[key] = value.split(" ");
              }
              if (current === "proxySettings") {
                reportPartObj[current] = {
                  ...reportPartObj[current],
                  [key]: value,
                };
              }
              break;

            case "terminalSettings":
            case "devices":
              if (reportPart === "devices" && key === "device") {
                reportPartObj.push({ [key]: value });
                break;
              }
              reportPartObj.at(-1)[key] = value;
              break;

            case "servicesInfo":
              if (line.includes("System: ACPI")) {
                current = "systemTree";
                reportPartObj[current] = [];
              }
              if (current) {
                reportPartObj[current] =
                  current === "systemTree"
                    ? [...reportPartObj[current], line]
                    : { ...reportPartObj[current], [key]: value };
              } else {
                reportPartObj[key] = value;
              }
              break;

            default:
              reportPartObj[key] = value;
              break;
          }
        } else if (currentObjPattern.test(line)) {
          current = camelize(line.trim().replace(":", ""));

          switch (reportPart) {
            case "fileChecksums":
            case "devices":
            case "licenseRelatedHardware":
              if (
                reportPart === "licenseRelatedHardware" &&
                current !== "hardDrives"
              ) {
                break;
              }
              reportPartObj[current] = [];
              break;

            case "servicesInfo":
              if (
                current === "ccdDatabaseCurrent" ||
                current === "ccdActivePaths"
              ) {
                reportPartObj[current] = [];
                break;
              } else {
                reportPartObj[current] = {};
                break;
              }

            case "terminalSettings":
              reportPartObj.push({});
              break;

            default:
              reportPartObj[current] = {};
              break;
          }
        } else {
          if (reportPart === "fileChecksums" && line !== "") {
            reportPartObj[current].push(line);
          }

          if (reportPart === "licenseRelatedHardware") {
            if (current === "proxySettings") {
              if (line === "No Proxy") {
                reportPartObj[current] = null;
              }
            } else {
              reportPartObj[current] = getHardwareData(
                line,
                reportPartObj[current]
              );
            }
          }

          if (reportPart === "servicesInfo") {
            reportPartObj[current].push(line);
          }

          if (line.match(versionPattern))
            reportPartObj = { ...reportPartObj, name: line };
        }
        data[reportPart] = reportPartObj;
        return { data, reportPart, current };
      }

      function getHardwareData(line, hardwareContainer) {
        entries = line
          .replace(/(?<=\]) (?=[^ ][\w/])(?=.*\])/g, "\r\n") // переносим ключи и значения в виде a[b] на новую строку
          .replace(/^([\w/]*)(\[)(.*)(\])/gm, "$1: $3") // a[b] ====> a: b
          .split(lineSeparator);

        hardwareItems = entries.reduce((item, subLine) => {
          const [key, value] = subLine.split(entriesSeparator);
          return { ...item, [key]: value };
        }, {});

        if (Array.isArray(hardwareContainer)) {
          return [...hardwareContainer, hardwareItems];
        }

        return {
          ...hardwareContainer,
          ...hardwareItems,
        };
      }

Начал переносить его на Typescript, но компилятор ругается.

В итоге у меня должен получиться объект с таким интерфейсом:

interface Report {
  devices: IDevice[];
  servicesInfo: IServicesInfo;
  fileChecksums: IFileChecksums;
  generalInfo: IGeneralInfo;
  licenseInfo: ILicenseInfo;
  licenseRelatedHardware: ILicenseRelatedHardware;
  terminalSettings: ITerminal[];
  windowsVersion: IWindowsVersion;
}

То есть, у меня переменная reportPart содержит строку, по которой осуществляется доступ к вложенному в Report объекту или массиву.

Начал писать код:

type ReportKeys = keyof Report;

type ReportObjectPart = IServicesInfo &
  IFileChecksums &
  IGeneralInfo &
  ILicenseInfo &
  ILicenseRelatedHardware &
  IWindowsVersion;

type ReportArrayPart = Array<ITerminal> & Array<IDevice>;

interface InitialData {
  reportPart: ReportKeys | null;
  current: string | null;
  data: Report;
}

const initialData: InitialData = { reportPart: null, current: null, data: { generalInfo: {} } as Report };

function parseTextStream(textStream: string) {
  const parsedData = textStream.split(lineSeparator).reduce((dataAcc: InitialData, line): InitialData => {
    let reportPart = Object.keys(dataAcc.data).at(-1) as ReportKeys;
    const current = null;
    const { data } = dataAcc;
    for (const item of reportParts) {
      if (line.includes(item)) {
        reportPart = camelize(item) as ReportKeys;
        if (reportPart === 'terminalSettings' || reportPart === 'devices') {
          data[reportPart] = [] as ReportArrayPart;
        } else {
          data[reportPart] = {} as ReportObjectPart;
        }
        return { reportPart, current, data };
      }
    }

    const parsedLine = parseLine(line, data, reportPart, current);
    return parsedLine;
  }, initialData);

  return parsedData.data;
}

function parseLine(line: string, data: Report, reportPart: ReportKeys, current: string | null) {
  let reportPartObj = data[reportPart];
  if (entriesPattern.test(line)) {
    const entries = line.trim().split(entriesSeparator);
    const [key, value] = entries;
    const parsedKey = camelize(key!.trim());
    let parsedValue: string | null | Date;

    if (value) {
      if (value === 'UNREGISTERED!' || value === 'empty' || value === 'set' || value === 'None') parsedValue = null;
      if (value && ISODatePattern.test(value)) parsedValue = new Date(value);
      parsedValue = value.trim();
    }

    switch (reportPart) {
      case 'licenseRelatedHardware':
        if (parsedKey === 'FINGERPRINT' || parsedKey === 'MACS') {
          reportPartObj[parsedKey] = parsedValue.split(' ');
        }
        if (current === 'proxySettings') {
          reportPartObj[current] = { ...reportPartObj[current], [parsedKey]: value };
        }
        break;

Дошел до этого, и здесь ошибка:

Element implicitly has an 'any' type because expression of type '"FINGERPRINT" | "MACS"'
can't be used to index type 'IDevice[] | IServicesInfo | IFileChecksums | IGeneralInfo | ILicenseInfo |
ILicenseRelatedHardware | ITerminal[] | IWindowsVersion'.
 
Property 'FINGERPRINT' does not exist on type 'IDevice[] | IServicesInfo | IFileChecksums | IGeneralInfo |
ILicenseInfo | ILicenseRelatedHardware | ITerminal[] | IWindowsVersion'.

Может кто подсказать, как разобраться с типами?


Ответы (0 шт):