Interface:
export interface User {
id: number;
name: string;
foo: string;
bar: string;
}
How do I check that a returned object from backend correctly implements User interface?
To declare a class that implements an interface, you include an implements clause in the class declaration. Your class can implement more than one interface, so the implements keyword is followed by a comma-separated list of the interfaces implemented by the class.
isInterface() method The isArray() method of the Class class is used to check whether a class is an interface or not. This method returns true if the given class is an interface. Otherwise, the method returns false , indicating that the given class is not an interface.
With the aim to make the code robust, i would like to check that the class implements the interface before instantiation / casting. I would like the the keyword 'instanceof' to verify a class implements an interface, as i understand it, it only verifies class type.
Here's another option: the module ts-interface-builder provides a build-time tool that converts a TypeScript interface into a runtime descriptor, and ts-interface-checker can check if an object satisfies it.
There isn't general way to do this. The general idea is to check if the object has the expected properties and they are of the expected types. Generally, if the output of the service is known, I would pick a few key differences to distinguish between the types of output and check only those.
Without more information an approach for this case would be:
function isUser(o: any) : o is User {
const u: User = o
return typeof u.id === "number"
&& typeof u.name === "string"
&& typeof u.foo === "string"
&& typeof u.bar === "string";
}
let o : any = {};
if(isUser(o)) {
console.log(o.id); // o is User
}
A more general approach that checks if an object has all the same properties as a sample object of the desired type would be:
function is<T>(o: any, sample:T, strict = true, recursive = true) : o is T {
if( o == null) return false;
let s = sample as any;
// If we have primitives we check that they are of the same type and that type is not object
if(typeof s === typeof o && typeof o != "object") return true;
//If we have an array, then each of the items in the o array must be of the same type as the item in the sample array
if(o instanceof Array){
// If the sample was not an arry then we return false;
if(!(s instanceof Array)) return false;
let oneSample = s[0];
let e: any;
for(e of o) {
if(!is(e, oneSample, strict, recursive)) return false;
}
} else {
// We check if all the properties of sample are present on o
for(let key of Object.getOwnPropertyNames(sample)) {
if(typeof o[key] !== typeof s[key]) return false;
if(recursive && typeof s[key] == "object" && !is(o[key], s[key], strict, recursive)) return false;
}
// We check that o does not have any extra prperties to sample
if(strict) {
for(let key of Object.getOwnPropertyNames(o)) {
if(s[key] == null) return false;
}
}
}
return true;
}
Example usage:
// A more complex interface
export interface User {
id: number;
name: string;
foo: string;
bar: string;
role: {
name: string;
id: number;
}
groups: Array<{
id: number,
name: string
}>;
}
// Returned from the service
let o : any = {
role : { name : "", id: 0 },
emails: ["a", "b"],
groups: [ { id: 0, name : ""} ],
bar: "", foo: "", id: 0, name: "",
};
// What properties will be checked.
const sampleUser: User = {
role : { name : "", id: 0 },
groups: [ { id: 0, name : ""} ],
emails : [""],
bar: "",
foo: "",
id: 0,
name: "",
};
if(is(o, sampleUser)){
console.log(o.id); // o is User
}
Note I have not tested the generic version in an extensive way, so expect some bugs and unhandled corner cases, but this should give you a good start if you want to go this route.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With