Динамические ключи объекта в 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'.
Может кто подсказать, как разобраться с типами?