Interface type check with Typescript

前端 未结 17 1009
灰色年华
灰色年华 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:42

    You can validate a TypeScript type at runtime using ts-validate-type, like so (does require a Babel plugin though):

    const user = validateType<{ name: string }>(data);
    
    0 讨论(0)
  • 2020-11-22 14:43

    I found an example from @progress/kendo-data-query in file filter-descriptor.interface.d.ts

    Checker

    declare const isCompositeFilterDescriptor: (source: FilterDescriptor | CompositeFilterDescriptor) => source is CompositeFilterDescriptor;
    

    Example usage

    const filters: Array<FilterDescriptor | CompositeFilterDescriptor> = filter.filters;
    
    filters.forEach((element: FilterDescriptor | CompositeFilterDescriptor) => {
        if (isCompositeFilterDescriptor(element)) {
            // element type is CompositeFilterDescriptor
        } else {
            // element type is FilterDescriptor
        }
    });
    
    0 讨论(0)
  • 2020-11-22 14:45

    I would like to point out that TypeScript does not provide a direct mechanism for dynamically testing whether an object implements a particular interface.

    Instead, TypeScript code can use the JavaScript technique of checking whether an appropriate set of members are present on the object. For example:

    var obj : any = new Foo();
    
    if (obj.someInterfaceMethod) {
        ...
    }
    
    0 讨论(0)
  • 2020-11-22 14:48

    Working with string literals is difficult because if you want to refactor you method or interface names then it could be possible that your IDE don't refactor these string literals. I provide you mine solution which works if there is at least one method in the interface

    export class SomeObject implements interfaceA {
      public methodFromA() {}
    }
    
    export interface interfaceA {
      methodFromA();
    }
    

    Check if object is of type interface:

    const obj = new SomeObject();
    const objAsAny = obj as any;
    const objAsInterfaceA = objAsAny as interfaceA;
    const isObjOfTypeInterfaceA = objAsInterfaceA.methodFromA != null;
    console.log(isObjOfTypeInterfaceA)
    

    Note: We will get true even if we remove 'implements interfaceA' because the method still exists in the SomeObject class

    0 讨论(0)
  • 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<string, string>;
    
        public constructor(keys: string[], types: string[]) {
            this.map = new Map<string, string>(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<T>(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<any>(_.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<any>(_.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<Bar>(foreign_object, Bar._checker)) { ... }
    
    0 讨论(0)
提交回复
热议问题