I have a data class
export interface MusicDto {
title?: string;
artist?: string;
//...
}
I would like to fill it with data from key/value tokens
tokens.forEach(token => {
const [key, value] = token.split(':');
if (key && value) {
const propertyName = key.trim().toLowerCase() as keyof MusicDto;
const propertyValue = value.replace(';', '').trim();
let targetProperty = musicData[propertyName];
if (targetProperty) {
switch (typeof targetProperty) {
case 'string': targetProperty = propertyValue;
break;
case 'number': targetProperty = parseFloat(propertyValue);
break;
default:
}
} else {
console.log(`Unknown property ${propertyName}`);
}
}
});
But all properties are going to "Unknown property"
You cannot fill an object this way by assigning to it's extracted value. You need to refer to the property directly. Also your initial class instance is totally empty so you can't get any runtime types from it.
A solution with fixes for these problems:
// Here the data
const anExampleVariable = `
#TITLE:ROOM;
#ARTIST:Blanc Bunny Bandit;
#BANNER:ROOM.png;
#BACKGROUND:ROOM-bg.png;
#CDTITLE:./CDTitles/DDR A20 PLUS.png;
#MUSIC:ROOM.ogg;
#SAMPLESTART:57.5;
#SAMPLELENGTH:15;
#BPMS:0=145;
`
const tokens = anExampleVariable.split('#').map(token => token.trim()).filter(token => token.length > 0);
// Here my target DTO
class MusicDto {
title?: string = '';
artist?: string = '';
banner?: string = '';
background?: string = '';
cdTitle?: string = '';
music?: string = '';
sampleStart?: number = 0;
sampleLength?: number = 0;
bpms?: string = '';
notes: number[][][] = [];
}
const musicData: MusicDto = new MusicDto()
const newData = tokens.reduce((r, item) => {
const [key, value] = item.split(':');
r[key.toLowerCase()] = value.slice(0,-1);
return r;
}, {} as Record<string, string>);
const foundKeys: string[] = [];
const keys = <T extends object>(obj: T) => Object.keys(obj) as (keyof T)[];
keys(musicData).forEach(name => {
const lower = name.toLowerCase();
if(lower in newData){
foundKeys.push(lower);
const value = newData[lower];
if(typeof musicData[name] === 'string'){
(musicData as any)[name] = value; // you could use a custom type guard
}else if(typeof musicData[name] === 'number'){
(musicData as any)[name] = parseFloat(value); // you could use a custom type guard
}
}
});
const notFoundKeys = Object.keys(newData).filter(k => !foundKeys.includes(k));
console.log('not found:', notFoundKeys);
console.log(musicData)