Interface type check with Typescript

前端 未结 17 1275
灰色年华
灰色年华 2020-11-22 14:09

This question is the direct analogon to Class type check with TypeScript

I need to find out at runtime if a variable of type any implements an interface. Here\'s my

17条回答
  •  一生所求
    2020-11-22 14:49

    Here's the solution I came up with using classes and lodash: (it works!)

    // TypeChecks.ts
    import _ from 'lodash';
    
    export class BakedChecker {
        private map: Map;
    
        public constructor(keys: string[], types: string[]) {
            this.map = new Map(keys.map((k, i) => {
                return [k, types[i]];
            }));
            if (this.map.has('__optional'))
                this.map.delete('__optional');
        }
    
        getBakedKeys() : string[] {
            return Array.from(this.map.keys());
        }
    
        getBakedType(key: string) : string {
            return this.map.has(key) ? this.map.get(key) : "notfound";
        }
    }
    
    export interface ICheckerTemplate {
        __optional?: any;
        [propName: string]: any;
    }
    
    export function bakeChecker(template : ICheckerTemplate) : BakedChecker {
        let keys = _.keysIn(template);
        if ('__optional' in template) {
            keys = keys.concat(_.keysIn(template.__optional).map(k => '?' + k));
        }
        return new BakedChecker(keys, keys.map(k => {
            const path = k.startsWith('?') ? '__optional.' + k.substr(1) : k;
            const val = _.get(template, path);
            if (typeof val === 'object') return val;
            return typeof val;
        }));
    }
    
    export default function checkType(obj: any, template: BakedChecker) : obj is T {
        const o_keys = _.keysIn(obj);
        const t_keys = _.difference(template.getBakedKeys(), ['__optional']);
        return t_keys.every(tk => {
            if (tk.startsWith('?')) {
                const ak = tk.substr(1);
                if (o_keys.includes(ak)) {
                    const tt = template.getBakedType(tk);
                    if (typeof tt === 'string')
                        return typeof _.get(obj, ak) === tt;
                    else {
                        return checkType(_.get(obj, ak), tt);
                    }
                }
                return true;
            }
            else {
                if (o_keys.includes(tk)) {
                    const tt = template.getBakedType(tk);
                    if (typeof tt === 'string')
                        return typeof _.get(obj, tk) === tt;
                    else {
                        return checkType(_.get(obj, tk), tt);
                    }
                }
                return false;
            }
        });
    }
    

    custom classes:

    // MyClasses.ts
    
    import checkType, { bakeChecker } from './TypeChecks';
    
    class Foo {
        a?: string;
        b: boolean;
        c: number;
    
        public static _checker = bakeChecker({
            __optional: {
                a: ""
            },
            b: false,
            c: 0
        });
    }
    
    class Bar {
        my_string?: string;
        another_string: string;
        foo?: Foo;
    
        public static _checker = bakeChecker({
            __optional: {
                my_string: "",
                foo: Foo._checker
            },
            another_string: ""
        });
    }
    

    to check the type at runtime:

    if (checkType(foreign_object, Bar._checker)) { ... }
    

提交回复
热议问题