Check if object correctly implements interface

前端 未结 3 838
予麋鹿
予麋鹿 2020-12-03 22:49

Interface:

export interface User {
  id: number;
  name: string;
  foo: string;
  bar: string;
}

How do I check that a returned object from

相关标签:
3条回答
  • 2020-12-03 23:24

    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.

    0 讨论(0)
  • 2020-12-03 23:25

    Here is an example:

    getUser(user: User) {
        return user;
    }
    

    Just make sure that you pass a user of type User in your function

    0 讨论(0)
  • 2020-12-03 23:30

    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.

    0 讨论(0)
提交回复
热议问题