1

I'm struggling with a React + TypeScript "problem": it actually don't stop my app, but now I've this doubt I want to remove!.. It's more of a question!

What I would like to achieve is to define the properties name of an object, whereas the properties name are given by variables. Let me explain with an example -> https://codepen.io/Gesma94/pen/xmwMzY

/* The 'data' property is "dynamic": it will always have this structure,
 * but the name properties (and the values) may change.
 * The 'mapping' property is used to know the name properties inside 'data'.
 */  
const series = {
  data: [
    {map1: "People reached", map2: 200},
    {map1: "People who called", map2: 117},
    {map1: "Contract signed", map2: 77}
  ],
  mapping: {
    stage: "map1",
    values: "map2"
  }
};

/* This is the actual app. */
const myPar = document.getElementById("myPar");
const { stage: stageLabel, values: valuesLabel} = series.mapping;

series.data.forEach((sample: any, index: number) => {
  myPar.innerHTML = myPar.innerHTML + "<br>" + sample[stageLabel];
});

As you can see, in the forEach I write sample: any.

Now, I know sample has a string (the first one will always be a string) property which name is map1, and that's because I retrieved and saved in stageLabel. I also know that the other property is a number (it's always a number) and its name map2 is saved in valueLabel.

Now, what I'm starving to understand is how to not write sample: any, but define the type of sample using the information I retrieved earlier. Basically, I would like to write something like: sample: {stageLabel: string, valueLabel: number}, but doing this way TypeScript believe sample has the properties stageLabel and valueLabel, since we are in "compile time" and it can't even evaluate the variables.

So, since TypeScript can't know what's inside the variables, I know I can't actually obtain sample: {map1: string, map2: number}, but maybe I can obtain some sort of aliasing. For example, I write sample: {stageLabel: string, valueLabel: number}, and in the rest of the code I use the properties sample.stageLabel and sample.valueLabel. Then, when the application is running, those property name are evaluated, so sample.stageLabel is actually sample.map1.

I hope I've been clear enough to understand my question.

1 Answer 1

2

Why not actually map your data?

const series = {
  data: [
    {map1: "People reached", map2: 200},
    {map1: "People who called", map2: 117},
    {map1: "Contract signed", map2: 77}
  ],
  mapping: {
    stage: "map1",
    values: "map2"
  }
};    

const myPar = document.getElementById("myPar");

let mappedData = getMappedData(series.data, series.mapping);

// append results
mappedData.forEach((record:{stage:string, value:number}) => {
  myPar.innerHTML = myPar.innerHTML + `<br>${record.stage}: ${record.value}`;
});


function getMappedData(data, mapping): {stage:string, value:number}[] {
    let stageLabel = mapping["stage"];
    let valueLabel = mapping["values"];

    return data.map(x => {        
        return { stage: x[stageLabel], value: x[valueLabel] };
    })
}
<p id="myPar"><b>Results:</b></p>

Note that the compiler already nows that items in mappedData have the type {stage:string, value:number}, so you can omit this when appending and simply write:

// append results
mappedData.forEach(record => {
  myPar.innerHTML = myPar.innerHTML + `<br>${record.stage}: ${record.value}`;
});
Sign up to request clarification or add additional context in comments.

1 Comment

I guess that would work: I will have my mapped data with the TypeScript checkings, so thanks. Still, I wonder if something like I said (some sort of aliases) is possible in TypeScript. I'll mark your answer as correct if no one can give me an answer about this.. Thanks again :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.