How do I initialize a TypeScript object with a JSON object

前端 未结 16 864
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 08:30

I receive a JSON object from an AJAX call to a REST server. This object has property names that match my TypeScript class (this is a follow-on to this question).

Wha

16条回答
  •  Happy的楠姐
    2020-11-22 08:55

    My approach is slightly different. I do not copy properties into new instances, I just change the prototype of existing POJOs (may not work well on older browsers). Each class is responsible for providing a SetPrototypes method to set the prototoypes of any child objects, which in turn provide their own SetPrototypes methods.

    (I also use a _Type property to get the class name of unknown objects but that can be ignored here)

    class ParentClass
    {
        public ID?: Guid;
        public Child?: ChildClass;
        public ListOfChildren?: ChildClass[];
    
        /**
         * Set the prototypes of all objects in the graph.
         * Used for recursive prototype assignment on a graph via ObjectUtils.SetPrototypeOf.
         * @param pojo Plain object received from API/JSON to be given the class prototype.
         */
        private static SetPrototypes(pojo: ParentClass): void
        {
            ObjectUtils.SetPrototypeOf(pojo.Child, ChildClass);
            ObjectUtils.SetPrototypeOfAll(pojo.ListOfChildren, ChildClass);
        }
    }
    
    class ChildClass
    {
        public ID?: Guid;
        public GrandChild?: GrandChildClass;
    
        /**
         * Set the prototypes of all objects in the graph.
         * Used for recursive prototype assignment on a graph via ObjectUtils.SetPrototypeOf.
         * @param pojo Plain object received from API/JSON to be given the class prototype.
         */
        private static SetPrototypes(pojo: ChildClass): void
        {
            ObjectUtils.SetPrototypeOf(pojo.GrandChild, GrandChildClass);
        }
    }
    

    Here is ObjectUtils.ts:

    /**
     * ClassType lets us specify arguments as class variables.
     * (where ClassType == window[ClassName])
     */
    type ClassType = { new(...args: any[]): any; };
    
    /**
     * The name of a class as opposed to the class itself.
     * (where ClassType == window[ClassName])
     */
    type ClassName = string & {};
    
    abstract class ObjectUtils
    {
    /**
     * Set the prototype of an object to the specified class.
     *
     * Does nothing if source or type are null.
     * Throws an exception if type is not a known class type.
     *
     * If type has the SetPrototypes method then that is called on the source
     * to perform recursive prototype assignment on an object graph.
     *
     * SetPrototypes is declared private on types because it should only be called
     * by this method. It does not (and must not) set the prototype of the object
     * itself - only the protoypes of child properties, otherwise it would cause a
     * loop. Thus a public method would be misleading and not useful on its own.
     * 
     * https://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript
     */
    public static SetPrototypeOf(source: any, type: ClassType | ClassName): any
    {
        let classType = (typeof type === "string") ? window[type] : type;
    
        if (!source || !classType)
        {
            return source;
        }
    
        // Guard/contract utility
        ExGuard.IsValid(classType.prototype, "type", type);
    
        if ((Object).setPrototypeOf)
        {
            (Object).setPrototypeOf(source, classType.prototype);
        }
        else if (source.__proto__)
        {
            source.__proto__ = classType.prototype.__proto__;
        }
    
        if (typeof classType["SetPrototypes"] === "function")
        {
            classType["SetPrototypes"](source);
        }
    
        return source;
    }
    
    /**
     * Set the prototype of a list of objects to the specified class.
     * 
     * Throws an exception if type is not a known class type.
     */
    public static SetPrototypeOfAll(source: any[], type: ClassType): void
    {
        if (!source)
        {
            return;
        }
    
        for (var i = 0; i < source.length; i++)
        {
            this.SetPrototypeOf(source[i], type);
        }
    }
    }
    

    Usage:

    let pojo = SomePlainOldJavascriptObjectReceivedViaAjax;
    
    let parentObject = ObjectUtils.SetPrototypeOf(pojo, ParentClass);
    
    // parentObject is now a proper ParentClass instance
    

提交回复
热议问题