Как инициализировать объект TypeScript с помощью объекта JSON

199

Я получаю JSON-объект от AJAX-вызова к REST-серверу. Этот объект имеет имена свойств, которые соответствуют моему классу TypeScript (это продолжение этого вопроса ).

Каков наилучший способ его инициализации? Я не думаю, что это будет работать, потому что у класса (& JSON-объект) есть члены, которые являются списками объектов, и члены, которые являются классами, и у этих классов есть члены, которые являются списками и / или классами.

Но я бы предпочел подход, который ищет имена членов и присваивает их всем, создавая списки и создавая экземпляры классов по мере необходимости, поэтому мне не нужно писать явный код для каждого члена в каждом классе (есть много!)

Дэвид Тилен
источник
1
Почему вы спросили об этом снова (поскольку ответ, который я дал в другом вопросе, сказал, что это не сработает и что речь идет о копировании свойств в существующий объект)?
WiredPrairie
1
возможный дубликат Как я
приведу
3
@WiredPrairie - это другой вопрос, я спрашиваю, могу ли я пройтись по свойствам по одному и назначить их. Другие вопросы спрашивали, могу ли я это разыграть.
Дэвид Тилен
1
@WiredPrairie cont: Если вы продолжаете погружаться в свойства, пока не дойдете только до примитивных типов, то они могут быть назначены через.
Дэвид Тилен
2
Он по-прежнему копирует все значения, как я и предполагал. Там нет нового способа сделать это в TypeScript, поскольку это фундаментальный дизайн JavaScript. Для больших объектов вам может не потребоваться копировать какие-либо значения, а просто «воздействовать» на структуру данных.
WiredPrairie

Ответы:

189

Вот несколько быстрых снимков, чтобы показать несколько разных способов. Они ни в коем случае не являются «полными» и, как отказ от ответственности, я не думаю, что это хорошая идея сделать это так. Кроме того, код не слишком чистый, так как я набрал его довольно быстро.

Также как примечание: Конечно, десериализуемые классы должны иметь конструкторы по умолчанию, как это имеет место во всех других языках, где я знаю о десериализации любого рода. Конечно, Javascript не будет жаловаться, если вы вызовете конструктор не по умолчанию без аргументов, но тогда класс должен быть лучше подготовлен к нему (плюс, на самом деле это не будет «типизированный способ»).

Вариант № 1: нет информации во время выполнения вообще

Проблема с этим подходом состоит главным образом в том, что имя любого члена должно соответствовать его классу. Это автоматически ограничивает вас одним членом одного типа на класс и нарушает несколько правил хорошей практики. Я настоятельно рекомендую против этого, но просто перечислите это здесь, потому что это был первый «черновик», когда я написал этот ответ (именно поэтому и имена «Foo» и т. Д.).

module Environment {
    export class Sub {
        id: number;
    }

    export class Foo {
        baz: number;
        Sub: Sub;
    }
}

function deserialize(json, environment, clazz) {
    var instance = new clazz();
    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], environment, environment[prop]);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    baz: 42,
    Sub: {
        id: 1337
    }
};

var instance = deserialize(json, Environment, Environment.Foo);
console.log(instance);

Вариант № 2: имя свойства

Чтобы избавиться от проблемы в варианте № 1, нам нужна некоторая информация о типе узла в объекте JSON. Проблема в том, что в Typescript эти вещи являются конструкциями времени компиляции, и они нужны нам во время выполнения, но объекты времени выполнения просто не знают о своих свойствах, пока они не установлены.

Один из способов сделать это - сделать так, чтобы классы знали их имена. Это свойство также необходимо в JSON. На самом деле, вам нужно только это в JSON:

module Environment {
    export class Member {
        private __name__ = "Member";
        id: number;
    }

    export class ExampleClass {
        private __name__ = "ExampleClass";

        mainId: number;
        firstMember: Member;
        secondMember: Member;
    }
}

function deserialize(json, environment) {
    var instance = new environment[json.__name__]();
    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], environment);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    __name__: "ExampleClass",
    mainId: 42,
    firstMember: {
        __name__: "Member",
        id: 1337
    },
    secondMember: {
        __name__: "Member",
        id: -1
    }
};

var instance = deserialize(json, Environment);
console.log(instance);

Вариант № 3: явное указание типов элементов

Как указано выше, информация о типах членов класса недоступна во время выполнения - если только мы не сделаем ее доступной. Нам нужно сделать это только для не примитивных членов, и мы готовы пойти:

interface Deserializable {
    getTypes(): Object;
}

class Member implements Deserializable {
    id: number;

    getTypes() {
        // since the only member, id, is primitive, we don't need to
        // return anything here
        return {};
    }
}

class ExampleClass implements Deserializable {
    mainId: number;
    firstMember: Member;
    secondMember: Member;

    getTypes() {
        return {
            // this is the duplication so that we have
            // run-time type information :/
            firstMember: Member,
            secondMember: Member
        };
    }
}

function deserialize(json, clazz) {
    var instance = new clazz(),
        types = instance.getTypes();

    for(var prop in json) {
        if(!json.hasOwnProperty(prop)) {
            continue;
        }

        if(typeof json[prop] === 'object') {
            instance[prop] = deserialize(json[prop], types[prop]);
        } else {
            instance[prop] = json[prop];
        }
    }

    return instance;
}

var json = {
    mainId: 42,
    firstMember: {
        id: 1337
    },
    secondMember: {
        id: -1
    }
};

var instance = deserialize(json, ExampleClass);
console.log(instance);

Вариант № 4: подробный, но аккуратный способ

Обновление 01/03/2016: Как отметил @GameAlchemist в комментариях ( идея , реализация ), начиная с Typescript 1.7, решение, описанное ниже, может быть написано лучше с использованием декораторов классов / свойств.

Сериализация - это всегда проблема, и, на мой взгляд, лучший способ - это путь, который не самый короткий. Из всех вариантов это то, что я бы предпочел, потому что автор класса имеет полный контроль над состоянием десериализованных объектов. Если бы мне пришлось угадывать, я бы сказал, что все другие варианты рано или поздно доставят вам неприятности (если только в Javascript не найдется собственный способ решения этой проблемы).

Действительно, следующий пример не делает гибкость справедливой. Это действительно просто копирует структуру класса. Разница, которую вы должны иметь в виду, заключается в том, что у класса есть полный контроль над использованием любого вида JSON, который он хочет контролировать с помощью состояния всего класса (вы можете вычислять вещи и т. Д.).

interface Serializable<T> {
    deserialize(input: Object): T;
}

class Member implements Serializable<Member> {
    id: number;

    deserialize(input) {
        this.id = input.id;
        return this;
    }
}

class ExampleClass implements Serializable<ExampleClass> {
    mainId: number;
    firstMember: Member;
    secondMember: Member;

    deserialize(input) {
        this.mainId = input.mainId;

        this.firstMember = new Member().deserialize(input.firstMember);
        this.secondMember = new Member().deserialize(input.secondMember);

        return this;
    }
}

var json = {
    mainId: 42,
    firstMember: {
        id: 1337
    },
    secondMember: {
        id: -1
    }
};

var instance = new ExampleClass().deserialize(json);
console.log(instance);
Инго Бюрк
источник
12
Вариант № 4 - это то, что я бы назвал разумным путем. Вам все еще нужно написать код десериализации, но он находится в том же классе и полностью управляем. Если вы пришли из Java, то это сравнимо с необходимостью написания equalsили toStringметодов (только то, что они обычно генерируются автоматически). Не должно быть слишком сложно написать генератор, deserializeесли вы этого хотите, но это просто не может быть автоматизация во время выполнения.
Инго Бюрк
2
@ IngoBürk, я знаю, что задаю этот вопрос спустя 2 года, но как это будет работать с массивом объектов? Приведенный выше пример кода отлично работает для объекта JSON. как это можно использовать для массива объектов?
Пратик Гайквад
2
Дополнительное замечание: начиная с версии 1.7 (по общему признанию, более поздней, чем ваш ответ), машинопись предоставляет декораторы классов / свойств, которые позволяют писать четвертое решение более аккуратно.
GameAlchemist
1
Лучшая документация, которую я нашел, - это ответ StackOverflow: stackoverflow.com/a/29837695/856501 . Я использовал декораторы в своем проекте, и хотя мне хотелось бы добавить несколько других функций, я должен сказать, что они работают как шарм.
GameAlchemist
2
Я бы пока не стал использовать декораторы для производственного проекта - имейте в виду, что они все еще являются экспериментальной функцией. Я бы не стал основывать реальный код на «экспериментах», потому что, насколько нам известно, они могут исчезнуть в следующей версии, и вам придется переписать кучу кода или навсегда застрять в старой версии TS. Просто мои $ .02
RVP
35

вы можете использовать, Object.assignя не знаю, когда это было добавлено, я в настоящее время использую Typescript 2.0.2, и это, кажется, функция ES6.

client.fetch( '' ).then( response => {
        return response.json();
    } ).then( json => {
        let hal : HalJson = Object.assign( new HalJson(), json );
        log.debug( "json", hal );

вот HalJson

export class HalJson {
    _links: HalLinks;
}

export class HalLinks implements Links {
}

export interface Links {
    readonly [text: string]: Link;
}

export interface Link {
    readonly href: URL;
}

вот что говорит хром

HalJson {_links: Object}
_links
:
Object
public
:
Object
href
:
"http://localhost:9000/v0/public

так что вы можете видеть, что это не делает назначение рекурсивно

xenoterracide
источник
2
так что, в основном, это так Object.assign. Почему тогда у нас есть два лексиконоподобных ответа над этим?
phil294
18
@Blauhim Потому Object.assignчто не будет работать рекурсивно и не будет создавать экземпляры правильных типов объектов, оставляя значения в качестве Objectэкземпляров. Хотя это подходит для тривиальных задач, сериализация сложных типов невозможна. Например, если свойство класса имеет пользовательский тип класса, JSON.parse+ Object.assignбудет создавать экземпляр этого свойства для Object. Побочные эффекты включают отсутствующие методы и средства доступа.
Джон Вайс
@JohnWeisz класс присвоения объектов верхнего уровня действительно имеет правильный тип, и я упомянул рекурсивную вещь в этом ... который сказал, YMMV, и это могут быть прерыватели сделок.
ксенотеррацид
Процитировано прямо из вопроса: «у класса есть члены, которые являются списками объектов, и члены, которые являются классами, и у этих классов есть члены, которые являются списками и / или классами [...] Я бы предпочел подход, который ищет член присваивает им имена и присваивает их, создавая списки и создавая экземпляры классов по мере необходимости, поэтому мне не нужно писать явный код для каждого члена в каждом классе " - это не так Object.assign, когда все еще сводится к написанию вложенных экземпляров с помощью рука. Этот подход хорош для очень простых объектов учебного уровня, но не для реального использования.
Джон Вайс
@JohnWeisz уверен, в основном отвечал этим, потому что это не было ни в каких ответах и ​​казалось простым для некоторых случаев использования. Я уверен, что это также может быть использовано в сочетании с другими ответами, такими как рефлексия, чтобы сделать то, что вы ищете. Я также написал это частично, чтобы помнить это позже. Глядя на эти ответы и использовав и написав гораздо более мощные библиотеки, кажется, что нет ничего доступного для «реального использования».
ксенотеррацид
34

TLDR: TypedJSON (рабочее доказательство концепции)


Корень сложности этой проблемы в том, что нам нужно десериализовать JSON во время выполнения, используя информацию о типах, которая существует только во время компиляции . Это требует, чтобы информация о типах была как-то доступна во время выполнения.

К счастью, это можно решить очень элегантным и надежным способом с помощью декораторов и ReflectDecorators :

  1. Используйте декораторы свойств для свойств, которые подлежат сериализации, для записи информации метаданных и сохранения этой информации где-нибудь, например, в прототипе класса
  2. Передайте эту информацию метаданных рекурсивному инициализатору (десериализатору).

 

Тип записи-информация

С помощью комбинации ReflectDecorators и декораторов свойств можно легко записать информацию о типе свойства. Элементарная реализация этого подхода будет:

function JsonMember(target: any, propertyKey: string) {
    var metadataFieldKey = "__propertyTypes__";

    // Get the already recorded type-information from target, or create
    // empty object if this is the first property.
    var propertyTypes = target[metadataFieldKey] || (target[metadataFieldKey] = {});

    // Get the constructor reference of the current property.
    // This is provided by TypeScript, built-in (make sure to enable emit
    // decorator metadata).
    propertyTypes[propertyKey] = Reflect.getMetadata("design:type", target, propertyKey);
}

Для любого данного свойства приведенный выше фрагмент добавит ссылку на функцию-конструктор свойства к скрытому __propertyTypes__свойству в прототипе класса. Например:

class Language {
    @JsonMember // String
    name: string;

    @JsonMember// Number
    level: number;
}

class Person {
    @JsonMember // String
    name: string;

    @JsonMember// Language
    language: Language;
}

И все, у нас есть требуемая информация о типе во время выполнения, которая теперь может быть обработана.

 

Тип обработки информации

Сначала нам нужно получить Objectэкземпляр, используя JSON.parse- после этого мы можем перебрать все входы в __propertyTypes__(собранные выше) и создать соответствующие свойства соответственно. Тип корневого объекта должен быть указан, чтобы десериализатор имел отправную точку.

Опять же, мертвой простой реализацией этого подхода будет:

function deserialize<T>(jsonObject: any, Constructor: { new (): T }): T {
    if (!Constructor || !Constructor.prototype.__propertyTypes__ || !jsonObject || typeof jsonObject !== "object") {
        // No root-type with usable type-information is available.
        return jsonObject;
    }

    // Create an instance of root-type.
    var instance: any = new Constructor();

    // For each property marked with @JsonMember, do...
    Object.keys(Constructor.prototype.__propertyTypes__).forEach(propertyKey => {
        var PropertyType = Constructor.prototype.__propertyTypes__[propertyKey];

        // Deserialize recursively, treat property type as root-type.
        instance[propertyKey] = deserialize(jsonObject[propertyKey], PropertyType);
    });

    return instance;
}
var json = '{ "name": "John Doe", "language": { "name": "en", "level": 5 } }';
var person: Person = deserialize(JSON.parse(json), Person);

Приведенная выше идея имеет большое преимущество десериализации ожидаемыми типами (для сложных / объектных значений) вместо того, что присутствует в JSON. Если Personожидается, что это Personэкземпляр, который создан. С некоторыми дополнительными мерами безопасности для примитивных типов и массивов этот подход можно сделать безопасным, который противостоит любому вредоносному JSON.

 

Краевые Чехлы

Однако, если вы теперь довольны тем, что решение настолько простое, у меня есть плохие новости: существует огромное количество крайних случаев, о которых нужно позаботиться. Только некоторые из которых:

  • Массивы и элементы массива (особенно во вложенных массивах)
  • Полиморфизм
  • Абстрактные классы и интерфейсы
  • ...

Если вы не хотите возиться со всем этим (держу пари, что нет), я был бы рад порекомендовать рабочую экспериментальную версию проверки концепции, использующей этот подход, TypedJSON - которую я создал чтобы решить именно эту проблему, проблема, с которой я сталкиваюсь каждый день.

Из-за того, что декораторы все еще считаются экспериментальными, я бы не рекомендовал использовать их для производственного использования, но до сих пор это хорошо мне помогало.

Джон Вайс
источник
TypedJSON работал отлично; Большое спасибо за ссылку.
Нил
Отличная работа, вы нашли очень элегантное решение проблемы, которая беспокоила меня некоторое время. Я буду очень внимательно следить за вашим проектом!
Джон Стриклер
12

Я использовал этого парня, чтобы сделать работу: https://github.com/weichx/cerialize

Это очень просто, но мощно. Поддерживает:

  • Сериализация и десериализация целого дерева объектов.
  • Постоянные и временные свойства одного и того же объекта.
  • Крючки для настройки (де) логики сериализации.
  • Он может (де) сериализоваться в существующий экземпляр (отлично подходит для Angular) или генерировать новые экземпляры.
  • и т.п.

Пример:

class Tree {
  @deserialize public species : string; 
  @deserializeAs(Leaf) public leafs : Array<Leaf>;  //arrays do not need extra specifications, just a type.
  @deserializeAs(Bark, 'barkType') public bark : Bark;  //using custom type and custom key name
  @deserializeIndexable(Leaf) public leafMap : {[idx : string] : Leaf}; //use an object as a map
}

class Leaf {
  @deserialize public color : string;
  @deserialize public blooming : boolean;
  @deserializeAs(Date) public bloomedAt : Date;
}

class Bark {
  @deserialize roughness : number;
}

var json = {
  species: 'Oak',
  barkType: { roughness: 1 },
  leafs: [ {color: 'red', blooming: false, bloomedAt: 'Mon Dec 07 2015 11:48:20 GMT-0500 (EST)' } ],
  leafMap: { type1: { some leaf data }, type2: { some leaf data } }
}
var tree: Tree = Deserialize(json, Tree);
Андре
источник
6

Я создал инструмент, который генерирует интерфейсы TypeScript и «карту типов» во время выполнения для выполнения проверки типов во время выполнения по результатам JSON.parse: ts.quicktype.io

Например, учитывая этот JSON:

{
  "name": "David",
  "pets": [
    {
      "name": "Smoochie",
      "species": "rhino"
    }
  ]
}

quicktype создает следующий интерфейс TypeScript и карту типов:

export interface Person {
    name: string;
    pets: Pet[];
}

export interface Pet {
    name:    string;
    species: string;
}

const typeMap: any = {
    Person: {
        name: "string",
        pets: array(object("Pet")),
    },
    Pet: {
        name: "string",
        species: "string",
    },
};

Затем мы проверяем результат JSON.parseпо карте типа:

export function fromJson(json: string): Person {
    return cast(JSON.parse(json), object("Person"));
}

Я пропустил некоторый код, но вы можете попробовать quicktype для деталей.

Дэвид Сигел
источник
1
После многих часов исследований и попыток приложить руку к нескольким методам синтаксического анализа, я могу сказать, что это отличное решение - главным образом потому, что декораторы все еще экспериментальны. * Оригинальная ссылка не работает для меня; но ts.quicktype.io работает. * Преобразование JSON в JSON-схему является хорошим первым шагом.
Лекси Хэнкинс
3

Вариант № 5: Использование конструкторов Typescript и jQuery.extend

Это наиболее приемлемый метод: добавьте конструктор, который принимает в качестве параметра структуру json, и расширьте объект json. Таким образом, вы можете разобрать структуру json во всей модели приложения.

Нет необходимости создавать интерфейсы или перечислять свойства в конструкторе.

export class Company
{
    Employees : Employee[];

    constructor( jsonData: any )
    {
        jQuery.extend( this, jsonData);

        // apply the same principle to linked objects:
        if ( jsonData.Employees )
            this.Employees = jQuery.map( jsonData.Employees , (emp) => {
                return new Employee ( emp );  });
    }

    calculateSalaries() : void { .... }
}

export class Employee
{
    name: string;
    salary: number;
    city: string;

    constructor( jsonData: any )
    {
        jQuery.extend( this, jsonData);

        // case where your object's property does not match the json's:
        this.city = jsonData.town;
    }
}

В вашем обратном вызове ajax, где вы получаете компанию для расчета зарплаты:

onReceiveCompany( jsonCompany : any ) 
{
   let newCompany = new Company( jsonCompany );

   // call the methods on your newCompany object ...
   newCompany.calculateSalaries()
}
Энтони Бренельер
источник
откуда $.extendберутся?
whale_steward
@whale_steward Я предполагаю, что автор ссылается на библиотеку jQuery. В мире JavaScript очень часто кто-то использует jQuery.
Ник Рот
как импортировать это? достаточно просто включить его в html head?
whale_steward
да я обновляю ответ, чтобы заменить $ на jQuery. импортируйте jQuery.js в заголовок html, установите и добавьте @ types / jquery в ваш файл package.json, раздел devDependencies.
Энтони Бренельер
1
Обратите внимание, что в Javascript вы должны это сделать Object.assign, что удаляет эту зависимость от jQuery.
Леон Пеллетье
2

Для простых объектов мне нравится этот метод:

class Person {
  constructor(
    public id: String, 
    public name: String, 
    public title: String) {};

  static deserialize(input:any): Person {
    return new Person(input.id, input.name, input.title);
  }
}

var person = Person.deserialize({id: 'P123', name: 'Bob', title: 'Mr'});

Использование возможности определения свойств в конструкторе позволяет ему быть кратким.

Это дает вам типизированный объект (против всех ответов, которые используют Object.assign или какой-либо вариант, который дает вам объект) и не требует внешних библиотек или декораторов.

stevex
источник
1

Описанный выше 4-й вариант - это простой и приятный способ сделать это, который должен сочетаться с 2-м вариантом в случае, когда вам нужно обрабатывать иерархию классов, как, например, список членов, который является любым из вхождений подклассов Суперкласс Участника, например, Директор расширяет Участника или Студент расширяет Участника. В этом случае вы должны указать тип подкласса в формате json.

Ксавье Мехо
источник
1

JQuery .extend делает это для вас:

var mytsobject = new mytsobject();

var newObj = {a:1,b:2};

$.extend(mytsobject, newObj); //mytsobject will now contain a & b
Даниил
источник
1

лучшее, что я нашел для этой цели - это класс-трансформер. github.com/typestack/class-transformer

Вот как вы это используете:

Какой-то класс:

export class Foo {

    name: string;

    @Type(() => Bar)
    bar: Bar;

    public someFunction = (test: string): boolean => {
        ...
    }
}


import { plainToClass } from 'class-transformer';

export class SomeService {

  anyFunction() {
u = plainToClass(Foo, JSONobj);
 }

Если вы используете декоратор @Type, будут созданы и вложенные свойства.

Фабиан
источник
0

Возможно не актуальное, но простое решение:

interface Bar{
x:number;
y?:string; 
}

var baz:Bar = JSON.parse(jsonString);
alert(baz.y);

работать на сложные зависимости тоже !!!

Михайло Пилип
источник
9
Этот подход на самом деле не работает, как ожидалось. Если вы проверяете результаты времени выполнения, они bazбудут иметь тип, Objectа не тип. Bar.Это работает в этом простом случае, потому что Barне имеет методов (только примитивные свойства). Если бы Barбыл такой метод isEnabled(), этот подход потерпел бы неудачу, так как этот метод не был бы в сериализованной строке JSON.
Тодд
0

Еще один вариант использования заводов

export class A {

    id: number;

    date: Date;

    bId: number;
    readonly b: B;
}

export class B {

    id: number;
}

export class AFactory {

    constructor(
        private readonly createB: BFactory
    ) { }

    create(data: any): A {

        const createB = this.createB.create;

        return Object.assign(new A(),
            data,
            {
                get b(): B {

                    return createB({ id: data.bId });
                },
                date: new Date(data.date)
            });
    }
}

export class BFactory {

    create(data: any): B {

        return Object.assign(new B(), data);
    }
}

https://github.com/MrAntix/ts-deserialize

использовать как это

import { A, B, AFactory, BFactory } from "./deserialize";

// create a factory, simplified by DI
const aFactory = new AFactory(new BFactory());

// get an anon js object like you'd get from the http call
const data = { bId: 1, date: '2017-1-1' };

// create a real model from the anon js object
const a = aFactory.create(data);

// confirm instances e.g. dates are Dates 
console.log('a.date is instanceof Date', a.date instanceof Date);
console.log('a.b is instanceof B', a.b instanceof B);
  1. делает ваши занятия простыми
  2. инъекции доступны для заводов для гибкости
Энтони Джонстон
источник
0

Лично я предпочитаю вариант № 3 @Ingo Bürk. И я улучшил его коды для поддержки массива сложных данных и массива примитивных данных.

interface IDeserializable {
  getTypes(): Object;
}

class Utility {
  static deserializeJson<T>(jsonObj: object, classType: any): T {
    let instanceObj = new classType();
    let types: IDeserializable;
    if (instanceObj && instanceObj.getTypes) {
      types = instanceObj.getTypes();
    }

    for (var prop in jsonObj) {
      if (!(prop in instanceObj)) {
        continue;
      }

      let jsonProp = jsonObj[prop];
      if (this.isObject(jsonProp)) {
        instanceObj[prop] =
          types && types[prop]
            ? this.deserializeJson(jsonProp, types[prop])
            : jsonProp;
      } else if (this.isArray(jsonProp)) {
        instanceObj[prop] = [];
        for (let index = 0; index < jsonProp.length; index++) {
          const elem = jsonProp[index];
          if (this.isObject(elem) && types && types[prop]) {
            instanceObj[prop].push(this.deserializeJson(elem, types[prop]));
          } else {
            instanceObj[prop].push(elem);
          }
        }
      } else {
        instanceObj[prop] = jsonProp;
      }
    }

    return instanceObj;
  }

  //#region ### get types ###
  /**
   * check type of value be string
   * @param {*} value
   */
  static isString(value: any) {
    return typeof value === "string" || value instanceof String;
  }

  /**
   * check type of value be array
   * @param {*} value
   */
  static isNumber(value: any) {
    return typeof value === "number" && isFinite(value);
  }

  /**
   * check type of value be array
   * @param {*} value
   */
  static isArray(value: any) {
    return value && typeof value === "object" && value.constructor === Array;
  }

  /**
   * check type of value be object
   * @param {*} value
   */
  static isObject(value: any) {
    return value && typeof value === "object" && value.constructor === Object;
  }

  /**
   * check type of value be boolean
   * @param {*} value
   */
  static isBoolean(value: any) {
    return typeof value === "boolean";
  }
  //#endregion
}

// #region ### Models ###
class Hotel implements IDeserializable {
  id: number = 0;
  name: string = "";
  address: string = "";
  city: City = new City(); // complex data
  roomTypes: Array<RoomType> = []; // array of complex data
  facilities: Array<string> = []; // array of primitive data

  // getter example
  get nameAndAddress() {
    return `${this.name} ${this.address}`;
  }

  // function example
  checkRoom() {
    return true;
  }

  // this function will be use for getting run-time type information
  getTypes() {
    return {
      city: City,
      roomTypes: RoomType
    };
  }
}

class RoomType implements IDeserializable {
  id: number = 0;
  name: string = "";
  roomPrices: Array<RoomPrice> = [];

  // getter example
  get totalPrice() {
    return this.roomPrices.map(x => x.price).reduce((a, b) => a + b, 0);
  }

  getTypes() {
    return {
      roomPrices: RoomPrice
    };
  }
}

class RoomPrice {
  price: number = 0;
  date: string = "";
}

class City {
  id: number = 0;
  name: string = "";
}
// #endregion

// #region ### test code ###
var jsonObj = {
  id: 1,
  name: "hotel1",
  address: "address1",
  city: {
    id: 1,
    name: "city1"
  },
  roomTypes: [
    {
      id: 1,
      name: "single",
      roomPrices: [
        {
          price: 1000,
          date: "2020-02-20"
        },
        {
          price: 1500,
          date: "2020-02-21"
        }
      ]
    },
    {
      id: 2,
      name: "double",
      roomPrices: [
        {
          price: 2000,
          date: "2020-02-20"
        },
        {
          price: 2500,
          date: "2020-02-21"
        }
      ]
    }
  ],
  facilities: ["facility1", "facility2"]
};

var hotelInstance = Utility.deserializeJson<Hotel>(jsonObj, Hotel);

console.log(hotelInstance.city.name);
console.log(hotelInstance.nameAndAddress); // getter
console.log(hotelInstance.checkRoom()); // function
console.log(hotelInstance.roomTypes[0].totalPrice); // getter
// #endregion
Алиреза Этемади
источник
-1

вы можете сделать как ниже

export interface Instance {
  id?:string;
  name?:string;
  type:string;
}

и

var instance: Instance = <Instance>({
      id: null,
      name: '',
      type: ''
    });
Аюб Али Саркер
источник
Это на самом деле не создаст экземпляр ожидаемого типа объекта во время выполнения. Он будет работать, когда ваш тип имеет только примитивные свойства, но потерпит неудачу, когда у типа есть методы. Определения интерфейса также недоступны во время выполнения (только во время сборки).
Тодд
-1
**model.ts**
export class Item {
    private key: JSON;
    constructor(jsonItem: any) {
        this.key = jsonItem;
    }
}

**service.ts**
import { Item } from '../model/items';

export class ItemService {
    items: Item;
    constructor() {
        this.items = new Item({
            'logo': 'Logo',
            'home': 'Home',
            'about': 'About',
            'contact': 'Contact',
        });
    }
    getItems(): Item {
        return this.items;
    }
}
user8390810
источник
позвоните по контенту, как
показано
<a class="navbar-brand" href="#"> {{keyItems.key.logo}} </a>
user8390810
Это не похоже на «создание экземпляров классов по мере необходимости».
LexieHankins