Interface:
export interface User {
id: number;
name: string;
foo: string;
bar: string;
}
How do I check that a returned object from
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.
Here is an example:
getUser(user: User) {
return user;
}
Just make sure that you pass a user of type User in your function
Interfaces only exist at compile time in TypeScript, and you cannot use something like instanceof
to check it at runtime.
I think the only way is to write runtime checks, something like this:
function implement(obj: any): obj is User {
return 'id' in obj && typeof obj['id'] === 'number' &&
'name' in obj && typeof obj['name'] === 'string' &&
'foo' in obj && typeof obj['foo'] === 'string' &&
'bar' in obj && typeof obj['bar'] === 'string';
}
implement({
id: 1,
name: 3,
foo: "foo",
bar: "bar"
});
// returns false
implement({
name: "name,
foo: "foo",
bar: "bar"
});
// returns false
implement({
id: 1,
name: "name",
foo: "foo",
bar: "bar"
});
// returns true
Note that here I assumed that you are using strict null check, so null or undefined cannot be assigned to your fields. For the general case, the logic for checking will be more complicated.