0

I have created model(by interface) for my json response:

export interface Test {
    id: number;
    name: string;
}

and I have variable for data:

  public test: Test;

But when I get json response with more parameters like last_name which is not in interface I still can use it in code. How can I cast this json response to match only my model?

I tried assertion but It's not working, I still have access to all other properties.

GokuLess
  • 9
  • 1
  • 6
  • You should copy only specified properties to the variable from json response – Oleksandr Poshtaruk Feb 24 '18 at 12:10
  • Possible duplicate of [Learning TypeScript - Casting Types](https://stackoverflow.com/questions/44967356/learning-typescript-casting-types) – Igor Feb 24 '18 at 12:11
  • But when I have for example 20 properties and response have 40, it would be hard to write it manually. – GokuLess Feb 24 '18 at 12:12
  • maybe you could iterate over the properties of the interface and only assign these, see https://stackoverflow.com/questions/41959488/get-properties-of-class-in-typescript – Turo Feb 24 '18 at 12:28
  • but it doesn't make sense, so how should I make a model for my json response? – GokuLess Feb 24 '18 at 12:35

2 Answers2

1

Interfaces are known only at compile time. You can use classes instead and by declaring properties in constructor they will be known at runtime:

class Test {
  constructor(
    public id: number,
    public name: string
  ){}
}

Then you can create function that will return an instance of a given class and fill it with data:

function cast<T>(data: any, model: new (...args: any[]) => T ): T {
  const classInstance = new model();
  const classProps = Object.getOwnPropertyNames(classInstance);

  classProps.forEach(prop => classInstance[prop] = data[prop]);
  return classInstance;
}

You can use this function to map received data to given model:

this.http.get<Test>('someUrl').map(res => cast(res, Test))
Wilhelm Olejnik
  • 2,382
  • 3
  • 14
  • 21
0

I couldn't do it for an Interface, but using a class to achieve what you want to do, is possible.

Angular [Typescript]

class Award {
  name = "";
  category = "";
}

this.httpClient.get<Awards>("./assets/configs/award.json").subscribe(($:Award) => {
  // Pass an instance of the Class, you wish to receive your response as
  console.log(this.takeWhatIsNecessary(new Award(), $));
});

// Breadth First Search
// This function will go through keys, in any depth, and copy
// property values from corresponding keys from Response
// Into the target value
// And returns the target
takeWhatIsNecessary(target, response) {
const stack = [];

if (!response || !target) {
  return response;
}

stack.push({
  r: response,
  t: target
});

while (stack.length > 0) {
   const obj = stack.shift();

   for (const key in obj.t) {
     if (obj.t.hasOwnProperty(key) && obj.r.hasOwnProperty(key)) {
       if (typeof obj.t[key] === "object") {
         stack.push({
           t: obj.t[key],
           r: obj.r[key]
         });
       }
       obj.t[key] = obj.r[key];
     }
   }
 }
 return target;
}

Javascript Sample

var uglyJSON = '{"name":"Test Name","category":"Test Category","author":"Do Not","appendix":"Read This","important":{"take_this":"Awesome","not_this":"Wow"}}';

var uglyResponse = JSON.parse(uglyJSON);

var myCustomClassObj = {
  name: "",
  category: "",
  important: {
    take_this: ""
  }
};

console.log(takeWhatIsNecessary(myCustomClassObj, uglyResponse));

// Breadth First Search
function takeWhatIsNecessary(target, response) {
  var stack = [];

  if (!response || !target) {
    return response;
  }

  stack.push({
    r: response,
    t: target
  });

  while (stack.length > 0) {
    var obj = stack.shift();

    for (var key in obj.t) {
      if (obj.t.hasOwnProperty(key) && obj.r.hasOwnProperty(key)) {
        if (typeof obj.t[key] === "object") {
          stack.push({
            t: obj.t[key],
            r: obj.r[key]
          });
          continue;
        }
        obj.t[key] = obj.r[key];
      }
    }
  }

  return target;
}
Abhijit Kar ツ
  • 1,732
  • 1
  • 11
  • 24