Страницы

Поиск по вопросам

Показаны сообщения с ярлыком typescript. Показать все сообщения
Показаны сообщения с ярлыком typescript. Показать все сообщения

четверг, 9 апреля 2020 г.

Имитация множественного наследования

#наследование #typescript #множественное_наследование #mixins #прототипное_наследование

                    
Есть класс и функция-конструктор. Делается попытка реализовать класс, являющийся
чем-то типа потомка обоих. Точнее, методы из прототипа функции-конструктора копируются
в прототип класса-потомка, унаследованный от класса.

Да, я понимаю, что это не полноценное наследование, но для решения задачи этого хватает.

Проблема в другом. Как заставить тайпскрипт воспринимать копируемые методы?

class First {
  someMethod() {
    console.log('someMethod from First');
  }
}

function Second() {
  console.log('Second');
}

Second.prototype.doSmth = function () { 
  console.log('doSmth from Second');
}

interface IBoth {
  someMethod()
  doSmth()
}

class Both extends First /* implements IBoth */ {
  constructor() {
    console.log('constructor of Both');
    super();
    Second.call(this);
  }
}

for (let key in Second.prototype) {
  Both.prototype[key] = Second.prototype[key];
}


На самом деле, надо обеспечить видимость методов ещё на уровень дальше

class Final extends Both {
  doIt() {
    this.someMethod();
    //this.doSmth(); // Надо заставить видеть метод тут
    (this as any as IBoth).doSmth(); // Компилируется, но это ужас
  }
}


если при этом методы не будут видны из самого Both, то это годится.

Вот что я уже пробовал:


При попытке написать

class Both extends First implements IBoth {


возникает ошибка, что я не реализую методы интерфейса.
При переименовании Both в _Both и использовании

var Both = _Both as typeof _Both;


всё остаётся как было, что логично, поскольку тут никак не используется First.
При переименовании Both в _Both и использовании

var Both = _Both as typeof IBoth;


говорит, что не может найти имя IBoth.


Пробовал ещё несколько вариантов, но они совсем бредовые.
Что ещё можно сделать?



Попробовать можно тут: http://www.typescriptlang.org/Playground
Полный код для проверки

Запустить (код из правой панели) после добавления строки:

(new Final).doIt();


Вывод при запуске при раскомментированной строке this.doSmth();  

constructor of Both
Second
someMethod from First
doSmth from Second
doSmth from Second




PS: Этот вопрос на английском.
    


Ответы

Ответ 1



Интерфейс вообще не нужен, нужно просто объявить прототипное поле с нужным типом: doSmth: () => void Она видима как свойство, а не как метод, но это непринципиально. Полный код: class First { someMethod() { console.log('someMethod from First'); } } function Second() { console.log('Second'); } Second.prototype.doSmth = function () { console.log('doSmth from Second'); } class Both extends First { constructor() { console.log('constructor of Both'); super(); Second.call(this); } doSmth: () => void } for (let key in Second.prototype) { Both.prototype[key] = Second.prototype[key]; } class Final extends Both { doIt() { this.someMethod(); this.doSmth(); //Both.prototype.doSmth(); // ok //Final.prototype.doSmth(); // ok } } PS: Надо было гуглить не всяческие варианты с наследованием, а typescript class prototype variable - сразу нашёл подходящий вариант.

суббота, 7 марта 2020 г.

Type assertions

#typescript


Кто ни будь может на пальцах объяснить с нормальными примерами, что такое assertions
в typescript и где это применяется. В документации этому  вопросу, уделяется несколько
строк с не понятными примерами.

Вот пример:

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;


Не понятно что вообще в этом коде происходит.
    


Ответы

Ответ 1



В документации есть следующее: Type assertions – это возможность сказать компилятору "Доверься мне, я знаю что делаю". Type assertion похож на каст в других языках программирования, но не делает каких-либо специальных проверок или преобразований. Он никак не влияет на время выполнения и добавляется только для компилятора. TypeScript надеется, что ты уже сделал все проверки, которые нужны. Далее приводится пример: let someValue: any = "this is a string"; let strLength: number = (someValue as string).length; Из-за any пример возможно не очень удачен, так как для этого типа нет проверок, какие свойства у него вызываются, и можно вызвать любое свойство, и компилятор не укажет на ошибку, с другой стороны, ide не подскажет, что у someValue есть такое свойство, как length. В случае же с type assertions, автодополнение начнет работать. Более наглядным пример может стать, если заменить any, на {}. В этом случае let someValue: {} = "this is a string"; При попытке вызвать someValue.length компилятор покажет ошибку Property 'length' does not exist on type '{}' И в этом случае использование type assertions становится понятнее: мы знаем, что в данной переменной будет строка, у которой есть свойство length и подсказываем об этом компилятору: (someValue as string).length или (someValue).length В этом случае компилятор верит, что эта переменная имеет строковое значение, а значит у нее можно взять свойство length.

пятница, 24 января 2020 г.

Использование в TypeScript классов .Net

#javascript #cpp #net #typescript #ecmascript_6


Есть идея иметь доступ из TypeScript к  методам и свойствам классов .Net.

У меня есть опыт вызова методов .Net объектов из натива https://habrahabr.ru/users/serginio1/topics/

И в частности Кроссплатформенное использование классов .Net из неуправляемого кода.
Или аналог IDispatch на Linux
   

Есть идея вызвать через Native Client Messaging System  

Там мы имеем взаимодействие JavaScript и натива через сообщения

В JavaScript мы можем подписаться на события

function handleMessage(message) {
  // In the example, we simply log the data that's received in the message.
  var logEl = document.getElementById('log');
  logEl.textContent += message.data;
}


И отправлять сообщения

var dictionary = {
  key:guid,
  MethodName: name,
  param_array: args
}
nacl_module.postMessage(dictionary);


На стороне натива

Можно принмать сообщения

virtual void HandleMessage(const pp::Var& var) {
  if (var.is_dictionary()) {
    pp::VarDictionary dictionary(var);
    // Use the dictionary
    pp::VarArray keys = dictionary.GetKeys();
    // ...
  } else {
    // ...
  }
}


Так и отправлять

pp::VarDictionary dictionary;
dictionary.Set(pp::Var("command"), pp::Var(next_command));
dictionary.Set(pp::Var("param_int"), pp::Var(123));
pp::VarArray an_array;
an_array.Set(0, pp::Var("string0"));
an_array.Set(1, pp::Var("string1"))
dictionary.Set(pp::Var("param_array"), an_array);
PostMessage(dictionary);


Все это можно сделать через Proxy 

class NetWrapper{

    public currentCount: any;

    public static dictionary = {};

    public proxy: any;

    public static wm = new WeakMap(); // Слабые ссылки на объекты
    public static refArray = new Array(); // ссылки на .Net объекты   
    constructor(private netRef:int) { this.SetProxy(); }


     private GetPromise(name: PropertyKey,args)
    {

        let key = Guid.newGuid();
        let promise = new Promise((resolve, reject) => {
            let item = new Item(resolve, args, name);
            this.dictionary[key] = item;

           var dictionary = {
           netRef:netRef,
           key:key,
           MethodName: name,
           param_array: args
         }
          nacl_module.postMessage(dictionary);
        });


        return promise;
    }


    private SetProxy(): void {

        let self=this;
        this.proxy = new Proxy({}, {

            get: function(rcvr: any, name: PropertyKey)
            {

               // Проблемы с определением, что свойство возвращает ссылку на метод
               // поэтому будем вызвать все как методы
               // if (этоСвойство)
               //     return self.GetPromise(name, []);

               return (...args) => {
                   return self.GetPromise(name,args)

                };


            }


        });
    }




// Пример вызова метода .Net

function handleMessage(message) {
  // In the example, we simply log the data that's received in the message.
  let data=message.data;
  let result=data.result;
  let item = this.dictionary[data.key];

  delete this.dictionary[key];

  if (data.resultIsObject)
   {

    let ref=result;
    result=new NetWrapper(ref);
    refArray.push(ref);
    wm.set(result,ref);
  }

  item.resolve(result);

}


Так как в в JavaScript нет финализаторов, то можно использовать WeakMap 

То есть можно было бы выгрузить значения из wm и сравнить их с текущим массивом ссылок
refArray. Если в WeakMap ссылок нет значит можно освободить ссылки и на стороне .Net

Но вроде как 


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


Использование такое

  let HttpClient=await NetWrap.GetType("System.Net.Http.HttpClient","System.Net.Http.dll");
     let HttpClientHandler = await NetWrap.GetType("System.Net.Http.HttpClientHandler","System.Net.Http.dll");

    let client=await NetWrap.new(HttpClient);
    let responst= await (await client.GetStringAsync("https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx")).Result();
 //Result вызываем как функцию


Можно для асинхронных методов result внутри Net сделать асинхронным

Буду рад любым советам. И может это никому и не нужно и не стоит вообще писать в стол?

Правда нашел ссылку на возможность финализатора https://www.npmjs.com/package/finalize

var finalize = require('finalize');
 
var obj = { x: 1337 };
finalize(obj, function () {
  console.log(this.x); // this will print '1337' 
});
global.gc();
// nothing will happen, var obj above holds obj alive 
obj = null;
global.gc();
// the previous line should trigger the callback above 


Кроме того для интегации мне посрветовали cef / JavaScriptIntegration 

Можно и без асинхронности  вызова обычных методов обойтись.

При этом можно зарегистрировать различные обработчики для асинхронных методов, для
событий

В итоге можно вызывать асинхронные методы например добавив кллючевое слово async,
а для свойств можно использовать @ в начале свойства

let url= "https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx");
let response= await client.async.GetStringAsync(url);
let response= client.GetStringAsync(url).@Result;

    


Ответы

Ответ 1



Сделал и работает на Angular 2 и TypeScript let Net = NetObject.NetWrapper; let del = NetObject.FlagDeleteObject; //let SB = Net.Новый("System.Text.StringBuilder"); let StringBuilder = Net.Тип("System.Text.StringBuilder"); let SB = new StringBuilder(); this.AddComment("SB", SB); var res = SB.Append("Первая строка"); res(del); var ToStr = SB.ToString(); this.AddComment("Tostring=", ToStr); SB.Capacity=40; res = SB._Capacity; this.AddComment("_Capacity=", res); SB(del); let Тестовый = Net.Тип("TestDllForCoreClr.Тестовый", "TestDllForCoreClr"); var TO = new Тестовый("Свойство из Конструктора"); var resAsync = await TO.async.GetStringAsync(); this.ResultExecute.push(new ResTest("GetStringAsync", resAsync)); res = TO.ДженерикМетод(1, 2, 5); this.AddComment("ДженерикМетод с выводом типов ", res); let Int32 = Net.GetType("System.Int32"); res = TO.ДженерикМетод([Int32, Int32], 1, 2, 5); this.AddComment("ДженерикМетод с аргeментами типов ", res); var Dictionary2 = Net.GetType("System.Collections.Generic.Dictionary`2", "System.Collections"); var DyctionaryIS = Net.GetGenericType(Dictionary2, "System.Int32", "System.String"); var D = new DyctionaryIS(); res = TO.ДженерикМетод2(D, 99, "Hello"); this.AddComment("ДженерикМетод2 с выводом типов ", res); res = TO.ДженерикМетод2([Int32, "System.String"],D, 99, "Hello"); this.AddComment("ДженерикМетод2 с аргументами ", res); var task = TO.GetStringAsync(); res = await Net.async.ReturnParam(task); this.AddComment("выполнение полученной задачи асинхронно ", res); var array = Net.GetNetArray(1, "Привет", 4.4); for (let item of array) this.AddComment("Элемент ", item); var EO = TO.ПолучитьExpandoObject(); this.AddComment("Свойство ExpandoObject Имя ",EO._Имя); this.AddComment("Свойство ExpandoObject Число ", EO._Число); var делегат = EO._ВСтроку; this.AddComment("Результат вызова делегата Встроку ", делегат()); this.AddComment("Результат вызова как Метода ", EO.ВСтроку()); NetObject.DeleteNetObjets(Int32, task, D, DyctionaryIS, Dictionary2, TO, Тестовый, StringBuilder, array, EO, делегат); this.AddComment("Количество элементов в хранилище ", Net.КоличествоЭлементовВХранилище()); this.AddComment("Первый удаленный ", Net.FirstDeleted()); Отличие от C# это Для получения свойства нужно добавить "_" res = SB._Capacity; Для вызова асинхронного метода нужно добавить ключевое слово async var resAsync = await TO.async.GetStringAsync(); Для вызова дженерик метода, если нельзя вывести типы по параметрам то аргументы указываем в массиве res = TO.ДженерикМетод2([Int32, "System.String"],D, 99, "Hello"); Поддержку событий и передачу объектов JS добавлю позже. Буду рад любым предложениям и критике.   Какие примеры лучше дать для статьи? Выложил в итоге статью с примерами CEF, ES6, Angular 2, TypeScript использование классов .Net Core для расширения возможностей И еще CEF, Angular 2 использование событий классов .Net Core И еще CEF, ES6, Angular 2, WebPack 2 .Net Core десктопное приложение без серверной части

Ответ 2



Кроме того на RSDN мне посоветовали CEF удобное C++ API еще ссылка cef / JavaScriptIntegration Можно и без асинхронности вызова обычных методов обойтись. При этом можно зарегистрировать различные обработчики для асинхронных методов, для событий В итоге можно вызывать асинхронные методы например добавив кллючевое слово async, а для свойств можно использовать @ в начале свойства Кроме того можно переопределить new let HttpClient= NetWrap.GetType("System.Net.Http.HttpClient","System.Net.Http.dll"); let HttpClientHandler = NetWrap.GetType("System.Net.Http.HttpClientHandler","System.Net.Http.dll"); let client= new HttpClient(HttpClientHandler); let url= "https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx"); let response= await client.async.GetStringAsync(url); let response= client.GetStringAsync(url).@Result; для ref параметров можно использовать класс обертку class RefParam{ isRefparam=true; constructor(public Value:any); } Кроме того можно реализовать enumerate для доступа через for(let prop in proxy) Если кому интересно, то выложил статью CEF,Es6,Angular 2,TypeScript использование классов .Net Core для расширения возможностей

суббота, 11 января 2020 г.

Как лучше хранить entity?

#javascript #typescript


В общем первый подход был такой 

public entities: Entities.GameEntity[] = [];
public players: Entities.PlayerEntity[] = [];


Когда мне нужно найти entity делаю так 

public findPlayer(playerId: string) {

    let playerFounded: Entities.PlayerEntity = null;
    this.players.forEach((player: Entities.PlayerEntity)=> {
        if (player.client.socketId == playerId) {
            playerFounded = player;
        }
    });

    return playerFounded;
}


Второй подход был такой 

public entities: any = {};
public ids: number[] = [];


Добавление 

entityParams[0] - id of number

this.entities[entityParams[0]] = entity;
this.ids.push(Number(entityParams[0]));


Обход всех 

this.ids.forEach((entityId)=> {
   this.entities[entityId] 
});


ну и если нужно быстро достать то this.entities[entityId]

Как можно более лучше делать такие вещи, и какой из этих подходов лучше
    


Ответы

Ответ 1



Подобную задачу решал создавая специальный класс коллекцию Collection Вспомогательный интерфейс IKey interface IKey { key: string; } Интерфейс коллекции interface ICollection { add(value: T); get(key: string): T; remove(key: string); } И сама реализация. Индекс элемента в массиве храним в хэше, поэтому не нужно делать полный перебор массива. class Collection implements ICollection { private array: T[] = []; private keyToIndex: {[key: string]: number} = {}; add(value: T) { this.array.push(value); this.keyToIndex[value.key] = this.array.length - 1; } get(key: string): T { return this.array[this.keyToIndex[key]]; } remove(key: string) { this.array.splice(this.keyToIndex[key], 1); delete this.keyToIndex[key]; } }

Ответ 2



Если объем данных который будет храниться в массиве небольшой, то может выбирать любой вариант. А вот если данных будет большие, то будут проблемы с производительностью. Я разрабатываю приложения для мобильных устройств (cordova), так вот там у меня возникли эти проблемы (массив был около 1500 элементов). И чтобы решить их мы придумали вот такую хитрость: // recordKeyName {string} первичный ключ // record {any} элемент // records {any[]} массив для обработки setMapping: function (recordKeyName, record, records) { if (!records['maps']) { records['maps'] = {}; records.getRecordByKey = function (link) { return records.maps[link]; }; } records['maps'][record[recordKeyName]] = record; } // индексирование массива for (var i = 0; i <= items.length - 1; i++) { this.setMapping('LINK', items[i], items); } В дополнение к этому мы еще индексируем дополнительные поля, чтобы фильтрация происходила быстрей (в примере этого нет) В итоге получилось так, что при обычном просмотре массива время уходило примерно ~33 мс, а через индексы ~0,08

Как использовать новые геттер и сеттер?

#javascript #typescript #ecmascript_6


Помогите пожалуйста отрефакторить код. Я написал компонент, который представляет
из себя форму для отправки вопросов. Так же он выводит заданные на настоящий момент
вопросы (пока в консоль, но это не принципиально). Вместо базы данных для учебных целей
использую localstorage.

Итак, вот мой сервис, которые работает с "базой данных":

@Injectable()
export class QuestionsService {

    private questions: Question[];

  constructor() { 
    this.questions = localStorage.questions ? JSON.parse(localStorage.questions) : [];
  }

  getQuestions() {
    console.log('qqq');
    return this.questions;
  };  

  setQuestions(question): void {
    this.questions.push(question);
    localStorage.questions = JSON.stringify(this.questions);
  };    

}


Вот описанный тип:

export class Question {
    text: string;
    speakerId: number;
    dateHuman: string;
    dateUnix: string;
}


Вот компонент:

export class QuestionComponent implements OnInit {

  private questions: Question[] = [];

  constructor(private questionsService: QuestionsService) { }

  ngOnInit() {   
    this.getQuestions();
    console.log(this.questions);
  }   

  private sendQuestion(): void {   
    this.questionsService.setQuestions({
      "text": "42256yregd",
      "speakerId": 23,
      "dateHuman": "223 oct",
      "dateUnix": "22222"
    });
  };

  private getQuestions(): void {
    this.questionsService.getQuestions().map(question => {
      this.questions.push(question);
     });   
  };   

}


Эта система работает. Но мне хотелось бы использовать новые модные геттеры и сеттеры
из стандарта es6. Вот моя попытка:

Сервис:

    private questions: Question[];

  constructor() { 
    this.questions = localStorage.questions ? JSON.parse(localStorage.questions) : [];
  }

  get questions() {
    console.log('qqq');
    return this.questions;
  };  

  set questions(question): void {
    this.questions.push(question);
    localStorage.questions = JSON.stringify(this.questions);
  };


компонент:

....
..

  private sendQuestion(): void {   
    this.questionsService.questions = {
      "text": "42256yregd",
      "speakerId": 23,
      "dateHuman": "223 oct",
      "dateUnix": "22222"
    };
  };

  private getQuestions(): void {
    this.questionsService.questions().map(question => {
      this.questions.push(question);
     });   
  };  

....
....


Но консоль выводит ошибку:


  Duplicate identifier 'questions'.

    


Ответы

Ответ 1



Проблема у вас вот в чем: private questions: Question[]; // раз get questions() { // два set questions(question): void { У вас объявлено два свойства с одним именем. Так нельзя делать, о чем вам и говорит tsc. Эта проблема решается добавлением префикса к приватному свойству: private _questions: Question[]; get questions() { set questions(question): void {

Ответ 2



Здесь get questions() { ... return this.questions; }; вы через замыкание рекурсивно вызываете геттер questions. До возвращения значения дело не доходит, потому как превышается максимальный стек рекурсивного вызова функции. Интерпретатор понимает запись this.questions не как "обратись к свойству questions объекта this", а как "получи значение свойства questions объекта this". А получение значения происходит при помощи обращения к геттеру, который в данном случае имеет то же имя, что и само свойство questions, что и приводит к неверному результату (к "бесконечной" рекурсии). В конечном итоге вам выдается предупреждение об этом. Но. Так было бы, если бы интерпретатор самостоятельно не пресекал подобное поведение. На самом деле до рекурсивного вызова дело даже не доходит, поскольку интерпретатор (компилятор) сразу обнаруживает, что у вас в коде коллизия имен - имя свойства совпадает с именем геттера. В случае с сеттером, обращаясь внутри него к this.questions вы, опять же, обращаетесь не напрямую к свойству questions, а, из-за наличия геттера, сначала именно к нему [к геттеру]. Соответственно, по логике, и переопределяете вы тоже не свойство, а геттер. Но, повторюсь, судя по ошибке, которую вы получили, подобное поведение все-таки как-то чем-то перехватывается. В ином случае мы бы, возможно, довольно долго гадали, в чем же дело. Ну или просто получили бы ошибку вроде описанной выше. Решение проблемы В любом случае, ваша проблема решается путем разграничения имени свойства и его accessor'ов (геттеров и сеттеров). В идеальном случае стоит делать само свойство приватным, а геттер и сеттер - публичными. Но, поскольку ограничения доступа, как такового, EcmaScript не предусматривает (даже его 6-ая версия), то придется полагаться на добропорядочность тех, кто использует ваш код. А именно, дать свойству имя _questions, а getter'у и setter'у - просто questions. Таким образом вы убиваете сразу двух зайцев: избавляетесь от нежелательного поведения и явно указываете, что само свойство является приватным (ну или защищенным). Обращу ваше внимание, что, по-факту, вы можете использовать любое имя, а вариант с нижним подчеркиванием я привел в качестве наиболее правильно (по ряду причин, описанных выше). Исходя из всего сказанного, ваши геттер и сеттер должны выглядеть как-то так (соответственно): get questions() { console.log('qqq'); return this._questions; // добавлено нижнее подчеркивание }; set questions(question): void { this._questions.push(question); localStorage.questions = JSON.stringify(this.questions); };

пятница, 10 января 2020 г.

в чем заключается типизация в тайпскрипте?

#typescript


Всем привет. Изучаю тайпскрипт, и в первую очередь меня интересуют интерфейсы. Для
проверки того, что как они работают, я написал следующее:

interface I1 {
    stringProp: string;
    numProp: number;
}

printI1(i1: I1): void {
    console.log(i1);
    console.log(i1.numProp * 2);
}


От поведения этого интерфейсного типа я ожидаю следующего:
В stringProp может попасть только строка, а в в numprop - только число. Интерфейс
позволит мне абстрагироваться от полей того типа, который я привожу к нему.      

Ну хорошо, возьмем такой класс.

class T1 {
    public propNotExistedInInterface: boolean = true;
    public stringProp: any;
    public numProp: any;
}


И попробуем привести его к I1  следующим образом.

let a: T1 = new T1();
a.stringProp = 13;
a.numProp = "asd";

this.printI1(a);


И результат: 

Object { propNotExistedInInterface: true, stringProp: 13, numProp: "asd" }

NaN


Ни одного из моих ожиданий не оправдалось. Ни одного предупреждения транслятора.
Так в чем заключается хвалёная типизация? пробовал менять NoImplicitAny - один и тот
же результат.

Следующий пример:

Берем интерфейс:

interface INumberWithFuncProcessor {
    num: number;
    func(number): string;
}


Мы будем использовать его в качестве параметра метода (при этом допускаем ошибку):

useProcessor(param: INumberWithFuncProcessor): string {
    return param.func(param);  // ошибка, подразумевали param.num
}


И используем наш метод:

let something = this.useProcessor(
        {
            num: 12,
            func: (x) => { return x.toString() }
        });


При попытке вывести something на экран мы увидим [object Object]. Транслятор молчит.
По всей видимости в наш параметр интерфейсного типа попадает что-то вроде. 

{ num: number; func(object): string; }


Вопрос, как видите, риторический: есть ли статическая типизация в ts, или это просто
рекламный ход.
    


Ответы

Ответ 1



Типизация, очевидно, есть. Просто в данном случае происходят некорректные проверки. Если разобрать первый пример: Есть интерфейс, в котором указаны конкретные поля, и указанным полям проставлен конкретный тип. Что происходит дальше, объявляется класс, у которого присутствуют эти поля, но им задан тип any. Что такое any - это заглушка, которая показывает, что тип может быть любой. В следствие этого переменную типа any можно присвоить переменной любого типа. Что получилось в итоге, компилятор не проверяет реальный тип полей, так как типизация работает только во время компиляции, и чтобы определить какое именно из значений в итоге будет у поля, и, соответственно, какой тип, компилятору нужно выполнить всю программу. В общем случае это неразрешимая задача, и определение типа any отдается на откуп программисту. В то же время, если убрать одно из полей в классе T1, например stringProp, компилятор может проверить, что у объекта передаваемого класса, отсутствуют обязательное поле и кинет ошибку. А также, если вместо any использовать конкретные типы, например public stringProp: number; компилятор опять подскажет, что типы не совпадают. Примеры для проверки Что касается второго примера: interface INumberWithFuncProcessor { num: number; func(number): string; } Ошибочно само описание интерфейса, и при выборе опции noImplicitAny компилятор подсказывает, что number - это параметр, и так как у него не указан напрямую тип, его тип будет any и как мы помним - any не проверяется в compile-time. Таким образом, вполне нормально передавать в него переменные любого типа. Если же указать конкретный тип параметра, например: interface INumberWithFuncProcessor { num: number; func(a:number): string; } То компилятор вполне резонно покажет ошибку: Argument of type 'INumberWithFuncProcessor' is not assignable to parameter of type 'number' Пример для проверки

пятница, 27 декабря 2019 г.

Angular 2 thinking and way

#typescript #angular2


Ситуация: 
Есть два очень похожих компонента: превью статьи на главной странице, и сама статья,
при переходе на "читать дальше". У компонента превью свой сервис, который тянет данные
через http. В роутере, при переходе на полноую статью (другой компонент) указан компонент
статьи, непосредственно. 

Вопрос: 
Создавать копмоненту свой собственный сервис, который будет дублировать код сервиса
превьюхи, либо же использовать один компонент, просто с отдельным шаблоном целой статьи
(//не нашел, как юзать несколько шаблонов в одном компоненте), либо же ваш вариант.
Интересует именно правильный подход с точки зрения angular way и TS.
Заранее благодарю.
    


Ответы

Ответ 1



Для обоих компонентов можно сделать через один сервис

суббота, 21 декабря 2019 г.

Как выбрать option в select по значению?

#html #html5 #angular2 #typescript #html_select


Возможно ли указать значение option, которое будет выбираться по умолчанию при загрузке
страницы? Пробовал через selected и  selected="selected". Результат нулевой.





    


Ответы

Ответ 1



Судя по разметке - используется Angular. При связывании через ngModel выбирается option чье значение совпадает со значением typeSearch. Таким образом достаточно просто присвоить этой переменной или свойству нужное значение.

Ответ 2



Можешь обойти циклом все значения select и применить порядковый номер соответствующего option (значение option совпадает с нужным значением) к select через selectedIndex. Пример: var defaultValue = "Расширению"; var yourSelect = document.querySelector('.form-control'); for (let i, j = 0; i = yourSelect.options[j]; j++) { if (i.value == defaultValue) { yourSelect.selectedIndex = j; break; } } Если нужного значения в select нет, то будет выбрано значение selected или же самое первое (при отсутствии selected).

суббота, 7 декабря 2019 г.

Как в SVG найти область обрамляемую путями

#javascript #алгоритм #svg #typescript


В SVG файле есть несколько путей path.

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

Это то, что получается в результате кнопки "Заливать замкнутые области" в Inkscape.

Возможно ли использовать библиотеки, предпочтительно SVG.js И её плагины.

Для чего мне это надо?

Хочу закрасить определённым образом, то что лежит внутри путей.

Пример: есть 4 пути.



Надо на JavaScript рассчитать, найти область внутри.



Может можно и без её расчёта сделать, но нужно что-то сделать, что бы я смог раскрасить
эту область на JavaScript.
    


Ответы

Ответ 1



Объединение нескольких path в один путь. В первом посте итак было два совершенно разных направления ответов, чтобы не запутать людей, которые возможно придут в этот топик, я сделаю дополнительный ответ по дополнительным комментариям автора вопроса. рассчитав точки пересечения, я могу вычислить и куски путей до этих пересечений, а потом объединить в один путь, хотя это конечно математически не совсем просто. Возьмем фигуру, составленную из четырех патчей, из предыдущего ответа: И объединим четыре патча из первого примера в один патч, который будет иметь четыре сабпатча (подпути). Надо только строго следить, чтобы каждый подпатч начинался с большой буквы "M" (абсолютное позиционирование начальной точки подпути). Копируем всё в один параметр Расчет длины суммарного патча,- используется метод - getTotalLength() JS
Расчетная длина суммарного патча составит - 2391px Когда приходит чёткое понимание, как можно сделать вручную объединение нескольких патчей в один, то наверное можно написать скрипт, который выполнит эту рутинную работу.

Ответ 2



Я не представляю, как можно объяснить плагину какой путь выбрать. Если только с помощью утилиты, где есть пипетка, например ColorMania навести её на определенный участок, скопировать код цвета #D62728 и затем по этому параметру найти конкретный путь, который закрашен этим цветом. Но это слишком непродуктивно. Я для себя выбрал другой, более короткий путь. Допустим у нас есть svg файл средней сложности: Ставлю единичку перед "М" в формуле пути d="1M83.21744517582593 и этот конкретный путь исчезает. Теперь я знаю какой конкретный путь отвечает за конкретную часть изображения. Если нужно одновременно перекрасить много участков с одинаковым цветом, то в Notepad++ делаю поиск с одновременной заменой по этому параметру #d62728 update по результатам уточнений в вопросе Взял ваш эскиз в качестве образца для SVG файла Ниже код svg Патчи не заполнены цветом. Только закрашены строки. Следующий пример с заполнением цветом. Видите как fill работает, если каждый конкретный path не замкнут,- распределение цвета идет по обе стороны от линии. Рассчитать точки пересечения патчей это одно, а вот вычислить, как будет распределен цвет, это уже более трудная задача и я не знаю, есть ли у неё простое решение. Так как просто рассчитать скриптом площадь такой криволинейной фигуры и то довольно сложная задача.

Ответ 3



В дополнение к ответу @Alexandr_TT ( суть которого в том, что несколько path-ей нужно собрать в один). Вижу довольно простое решение, которое однако может показаться математически не самым правильным: Растеризуем картинку с помощью canvas. Выполняем любую функцию заливки изображения цветом X (например самую простую постил здесь) в любой точке предполагаемой фигуры. Перебираем все пикселы цвета X в canvas, переводим координаты пикселов в координаты svg. Для каждого пиксела перебираем расстояние до каждой вершины в svg - и если оно меньше порогового (порог - размер пиксела x 2 в системе координат SVG) - значит мы нашли новую вершину нашей новой области! Осталось только последовательно соединить все найденные вершины - делается это просто, так как все отрезки записаны в path-ах. Если отрезок не найден - берётся крайняя ближайшая точка вне path-а: таким образом образуются стыки разных path-ей. Хорош своей простотой. Алгоритм #2: От конкретной точки предполагаемой фигуры проводятся воображаемые отрезки(назову их "лучи") до каждой из вершин каждого path-а. Для каждого луча проверяется пересечение с каждым отрезком каждого path-а (например таким алгоритмом проверки пересечения отрезков). Если луч не пересёк ни одного отрезка, бинго, вершина на конце луча - это одна из вершин фигуры, которую мы вычисляем. Таким образом перебрав все лучи мы получаем все вершины новой фигуры кроме угловых. Угловые вершины вычисляются отдельно пересечением угловых отрезков (т.к. они не относятся ни к одному из путей). Дальше сооственно все - последовательное расположение вершин будет новой фигурой. Хорош точностью. Есть одно большое но - заполнит только выпуклые фигуры - это решится усложнением алгоритма. В трёхмерной графике такие алгоритмы нередки для collision detection, это целая история - и оптимизировать их быстродействие можно годами :)

понедельник, 15 июля 2019 г.

Как рассчитать рикошет?

У меня есть два тела круглой формы, они движутся на встречу друг к другу и сталкиваются. При столкновении вектора их направления движения должны измениться. Как их рассчитать? (массы одинаковые, трения нет, размер не важен) V1={x1,y1} V2={x2,y2} N={x3,y3}

Мое предположение что так:

(x1*x3+y1*y3) / sqrt(x1^2 + x3^2) * sqrt(y1^2+y3^2) = получим число F и умножим так вектор V1 - (x1*F, y1*F)
Попробовал просто сложить вектора N+V1 вроде похоже получилось


Ответ

вобщем, сперва как уже сказали в комментариях, нужно определить пересекаются ли круги - для этого нужно добавить условие
if (distBetweebCirles <= circle1.radius + circle2.radius) { // collision happened // resolve it }
в вашем случае distBetweenCirles это длина вектора N(пожалуйста, обозначьте его как то подругому, например distanceVector), а радиус можно считать эквивалентным массе при симуляции.
Далее, допустим круги сталкиваются, так как массы одинаковые, то векторы скоростей просто обменяются друг с другом. Это следует из следующей формулы (которая также применима и для случая с разнымы массами)
1)
v1 = (u1 * (m1 - m2) + 2 * m2 * u2) / (m1 + m2)
v2 = (u2 * (m1 - m2) + 2 * m1 * u1) / (m1 + m2)
где u1, u2 векторы скоростей до столкновения, а v1, v2 после. И если m1 == m2 то получаем
2)
v1 = u2
и соотвественно
v2 = u1 (как это вышло выведите сами из формулы 1))
Откуда взялась первая формула ? - при таком упругом столкновении действуют закон сохранения импульса и закон сохранения энергии
следовательно, необходимо решить систему уравнений относительно v1 и v2
m1 * u1 + m2 * u2 = m1 * v1 + m2 * v2
(m1 * u1^2) / 2 + (m2 * u2^2) / 2 = (m1 * v1^2) / 2 + (m2 * v2^2) / 2
решение которого есть формула 1)
читать подробнее про мат часть тут

вторник, 9 июля 2019 г.

Es6 TypeScript arguments

Решил побаловаться TypeScript и es6, а в частности Proxy.
Проблема в том, что в отладчике (Edge) показывется и содержимое и длина arguments, а вот в коде все методы преобразования в массив возвращается [] или обращения к длине возвращает 0.
class Item { constructor(public resolve: any, public value: any) { }; }
class CounterComponent { public currentCount: any; private dictionary = {}; private proxy: any; constructor() { this.SetProxy(); } private SetProxy(): void {
this.proxy = new Proxy({}, {
get: (rcvr, name) => {
return () => {
let args: Array = []; let length = arguments.length; //length==0 let args1 = [...arguments]; //args1==[] for (let i = 0; i < arguments.length; i++) { args.push(arguments[i]);
}
let promise = new Promise((resolve, reject) => { // resolve(123); let item = new Item(resolve, args); this.dictionary[name] = item;
});
return promise;
};
}
}); } public async CallMethodAsync() {
this.currentCount = await this.proxy.AllHello("Всем Привет"); }
public setResult(): void {
let key: string; for (let name in this.dictionary) { key = name;
}
let item = this.dictionary[key]; item.resolve(item.value);
} }
При этом await отрабатывает. И еще есть ли аналог C# финализатор, что бы понять когда объект подвергается сборке мусора.


Ответ

Проблема заключается в том, что одно из ограничений стрелочных функций
Не имеет собственного объекта arguments Стрелочные функции не имеют собственного объекта arguments, поэтому в теле стрелочных функций arguments будет ссылаться на переменную в окружающей области.

Самое простое решение - использовать не стрелочную функцию, например
get: (rcvr, name) => { return function() { let args: Array = []; ... } }
Альтернативное решение: использовать rest-параметры
get: (rcvr, name) => { return (...args)=>{ ... } }
В этом случае args уже массив и с ним можно работать сразу без дополнительных операций.

понедельник, 8 июля 2019 г.

В чем разница между void и never в TypeScript?

В TypeScript есть два псевдо-типа данных обозначающих "ничто": void и never
Чем они отличаются и как понять когда применять void, а когда never?


Ответ

Псевдо-тип never означает, что функция никогда не возвращает значения. Такое может быть, например, в случае если функция всегда выбрасывает исключение:
function f(): never { throw new Error('Never return'); }
Псевдо-тип void означает, что функция возвращает undefined. Это происходит в случае, если поток выполнения доходит до конца функции, не встретив конструкции return
function f(arg): void { console.log(arg); }
или когда конструкция return используется без указания аргумента:
function f(arg): void { if (arg < 0) { return; } console.log(arg); }

Если говорить о выборе между void и never, то вам почти всегда нужно выбирать именно void. Все дело в том, что never используется в совсем уж экзотических ситуациях, поэтому его можно смело исключать из набора типов "на каждый день".

вторник, 25 июня 2019 г.

Имитация множественного наследования

Есть класс и функция-конструктор. Делается попытка реализовать класс, являющийся чем-то типа потомка обоих. Точнее, методы из прототипа функции-конструктора копируются в прототип класса-потомка, унаследованный от класса.
Да, я понимаю, что это не полноценное наследование, но для решения задачи этого хватает.
Проблема в другом. Как заставить тайпскрипт воспринимать копируемые методы?
class First { someMethod() { console.log('someMethod from First'); } }
function Second() { console.log('Second'); }
Second.prototype.doSmth = function () { console.log('doSmth from Second'); }
interface IBoth { someMethod() doSmth() }
class Both extends First /* implements IBoth */ { constructor() { console.log('constructor of Both'); super(); Second.call(this); } }
for (let key in Second.prototype) { Both.prototype[key] = Second.prototype[key]; }
На самом деле, надо обеспечить видимость методов ещё на уровень дальше
class Final extends Both { doIt() { this.someMethod(); //this.doSmth(); // Надо заставить видеть метод тут (this as any as IBoth).doSmth(); // Компилируется, но это ужас } }
если при этом методы не будут видны из самого Both, то это годится.
Вот что я уже пробовал:
При попытке написать
class Both extends First implements IBoth {
возникает ошибка, что я не реализую методы интерфейса. При переименовании Both в _Both и использовании
var Both = _Both as typeof _Both;
всё остаётся как было, что логично, поскольку тут никак не используется First При переименовании Both в _Both и использовании
var Both = _Both as typeof IBoth;
говорит, что не может найти имя IBoth
Пробовал ещё несколько вариантов, но они совсем бредовые. Что ещё можно сделать?

Попробовать можно тут: http://www.typescriptlang.org/Playground Полный код для проверки
Запустить (код из правой панели) после добавления строки:
(new Final).doIt();
Вывод при запуске при раскомментированной строке this.doSmth();
constructor of Both Second someMethod from First doSmth from Second doSmth from Second

PS: Этот вопрос на английском.


Ответ

Интерфейс вообще не нужен, нужно просто объявить прототипное поле с нужным типом:
doSmth: () => void
Она видима как свойство, а не как метод, но это непринципиально.
Полный код:
class First { someMethod() { console.log('someMethod from First'); } }
function Second() { console.log('Second'); }
Second.prototype.doSmth = function () { console.log('doSmth from Second'); }
class Both extends First { constructor() { console.log('constructor of Both'); super(); Second.call(this); }
doSmth: () => void }
for (let key in Second.prototype) { Both.prototype[key] = Second.prototype[key]; }
class Final extends Both { doIt() { this.someMethod(); this.doSmth(); //Both.prototype.doSmth(); // ok //Final.prototype.doSmth(); // ok } }
PS: Надо было гуглить не всяческие варианты с наследованием, а typescript class prototype variable - сразу нашёл подходящий вариант.

четверг, 20 июня 2019 г.

Как в TypeScript указать массив классов?

Например у меня есть класс Module и от него будут наследовать много классов. Например модуль User или модуль Session, и я хочу хранить все эти классы в одном месте.
class User extends Module {
}
api.addModule(User)
как мне описать тип аргумента принимаемый функцией addModule?
addModule (moduleClass:Function){ this.modules[moduleClass.name] = moduleClass }
moduleClass:Function??? фанкшен работает, но мне нужны не любые функции а именно классы, да еще и унаследованные от Module
более того я хочу создать функцию addModules
api.addModules([User, Session])
чтобы можно было принимать массив классов =) подскажите как такой параметр описать в тайпскрипте? чтобы как бы не инстанс класса, а именно сам класс или его классы наследники.


Ответ

для указания того, что нам нужен не инстанс класса, а сам класс, применяется конструкция: typeof Class
addModule(ModuleClass:typeof Module) { this.modules[module.name] = ModuleClass }
getModule(name:string):typeof Module { return this.modules[name] }
addModules(modules:typeof Module[]) {
}
моя ошибка заключалась в том что я использовал устаревший синтаксис типа typeof(Module) который я нагуглил в обсуждении разработчиков тайпскрипта которое они вели аж в 2012 году ^_^

среда, 19 июня 2019 г.

TypeScript, почему работает var v: Object = {name1: 'val1'}

Приветствую! Имеем TypeScript 1.8
Пишу так:
var v: Object; v = {name1: 'val1'}
Все хорошо компилируется. Вроде как Object совместим с любым объектом. Пишу вот так:
interface MyInterface { name2: string; } var v: MyInterface; v = {name1: 'val1', name2: 'val2'} // <--- РУГАЕТСЯ
И он ругается: Error:(138, 55) TS2322: Type '{ name1: 'val1', name2: 'val2' }' is not assignable to type 'MyInterface '. Object literal may only specify known properties, and 'name1' does not exist in type 'MyInterface '.
НО! Если только "known properties", то почему не ругается на первый вариант с Object? Ведь в lib.d.ts Object также определен как Interface:
interface Object {...
И, естественно, никакого name1 там также не описано.


Ответ

Окей, вопрос оказался сложнее. Заглядываем в спецификацию языка, раздел 3.11.5. Читаем:
A property P is said to be expected in a type T if one of the following is true: T is not an object, union, or intersection type. T is an object type and T has a property with the same name as P T has a string or numeric index signature, T has no properties, or T is the global type 'Object'. (***) T is a union or intersection type and P is expected in at least one of the constituent types of T
Таким образом, особое место Object'а прямо прописано в спецификации, в пункте, обозначенном тремя звёздочками. Также специально оговорен интерфейс без свойств.

Пример:
Это компилируется, потому что Object
var v: Object; v = {name1: 'val1', name2: 'val2'}
Это не компилируется из-за лишнего свойства name1
interface MyInterface1 { name2: string; }
var x1: MyInterface1; x1 = {name1: 'val1', name2: 'val2'}
Это компилируется, так как интерфейс без свойств:
interface MyInterface2 { }
var x2: MyInterface2; x2 = {name1: 'val1', name2: 'val2'}
Это не компилируется, лишнее свойство name1 и name2, исключения не применимы:
interface MyInterface3 extends Object { }
var x3: MyInterface3; x3 = {name1: 'val1', name2: 'val2'}
Это компилируется, так как есть index signature:
interface MyInterface4 { [x: string]: any; }
var x4: MyInterface4; x4 = {name1: 'val1', name2: 'val2'}

пятница, 1 марта 2019 г.

Как использовать новые геттер и сеттер?

Помогите пожалуйста отрефакторить код. Я написал компонент, который представляет из себя форму для отправки вопросов. Так же он выводит заданные на настоящий момент вопросы (пока в консоль, но это не принципиально). Вместо базы данных для учебных целей использую localstorage.
Итак, вот мой сервис, которые работает с "базой данных":
@Injectable() export class QuestionsService {
private questions: Question[];
constructor() { this.questions = localStorage.questions ? JSON.parse(localStorage.questions) : []; }
getQuestions() { console.log('qqq'); return this.questions; };
setQuestions(question): void { this.questions.push(question); localStorage.questions = JSON.stringify(this.questions); };
}
Вот описанный тип:
export class Question { text: string; speakerId: number; dateHuman: string; dateUnix: string; }
Вот компонент:
export class QuestionComponent implements OnInit {
private questions: Question[] = [];
constructor(private questionsService: QuestionsService) { }
ngOnInit() { this.getQuestions(); console.log(this.questions); }
private sendQuestion(): void { this.questionsService.setQuestions({ "text": "42256yregd", "speakerId": 23, "dateHuman": "223 oct", "dateUnix": "22222" }); };
private getQuestions(): void { this.questionsService.getQuestions().map(question => { this.questions.push(question); }); };
}
Эта система работает. Но мне хотелось бы использовать новые модные геттеры и сеттеры из стандарта es6. Вот моя попытка:
Сервис:
private questions: Question[];
constructor() { this.questions = localStorage.questions ? JSON.parse(localStorage.questions) : []; }
get questions() { console.log('qqq'); return this.questions; };
set questions(question): void { this.questions.push(question); localStorage.questions = JSON.stringify(this.questions); };
компонент:
.... ..
private sendQuestion(): void { this.questionsService.questions = { "text": "42256yregd", "speakerId": 23, "dateHuman": "223 oct", "dateUnix": "22222" }; };
private getQuestions(): void { this.questionsService.questions().map(question => { this.questions.push(question); }); };
.... ....
Но консоль выводит ошибку:
Duplicate identifier 'questions'.


Ответ

Проблема у вас вот в чем:
private questions: Question[]; // раз
get questions() { // два set questions(question): void {
У вас объявлено два свойства с одним именем. Так нельзя делать, о чем вам и говорит tsc. Эта проблема решается добавлением префикса к приватному свойству:
private _questions: Question[];
get questions() { set questions(question): void {

среда, 20 февраля 2019 г.

в чем заключается типизация в тайпскрипте?

Всем привет. Изучаю тайпскрипт, и в первую очередь меня интересуют интерфейсы. Для проверки того, что как они работают, я написал следующее:
interface I1 { stringProp: string; numProp: number; }
printI1(i1: I1): void { console.log(i1); console.log(i1.numProp * 2); }
От поведения этого интерфейсного типа я ожидаю следующего: В stringProp может попасть только строка, а в в numprop - только число. Интерфейс позволит мне абстрагироваться от полей того типа, который я привожу к нему.
Ну хорошо, возьмем такой класс.
class T1 { public propNotExistedInInterface: boolean = true; public stringProp: any; public numProp: any; }
И попробуем привести его к I1 следующим образом.
let a: T1 = new T1(); a.stringProp = 13; a.numProp = "asd";
this.printI1(a);
И результат:
Object { propNotExistedInInterface: true, stringProp: 13, numProp: "asd" }
NaN
Ни одного из моих ожиданий не оправдалось. Ни одного предупреждения транслятора. Так в чем заключается хвалёная типизация? пробовал менять NoImplicitAny - один и тот же результат.
Следующий пример:
Берем интерфейс:
interface INumberWithFuncProcessor { num: number; func(number): string; }
Мы будем использовать его в качестве параметра метода (при этом допускаем ошибку):
useProcessor(param: INumberWithFuncProcessor): string { return param.func(param); // ошибка, подразумевали param.num }
И используем наш метод:
let something = this.useProcessor( { num: 12, func: (x) => { return x.toString() } });
При попытке вывести something на экран мы увидим [object Object]. Транслятор молчит. По всей видимости в наш параметр интерфейсного типа попадает что-то вроде.
{ num: number; func(object): string; }
Вопрос, как видите, риторический: есть ли статическая типизация в ts, или это просто рекламный ход.


Ответ

Типизация, очевидно, есть. Просто в данном случае происходят некорректные проверки.
Если разобрать первый пример:
Есть интерфейс, в котором указаны конкретные поля, и указанным полям проставлен конкретный тип.
Что происходит дальше, объявляется класс, у которого присутствуют эти поля, но им задан тип any. Что такое any - это заглушка, которая показывает, что тип может быть любой. В следствие этого переменную типа any можно присвоить переменной любого типа.
Что получилось в итоге, компилятор не проверяет реальный тип полей, так как типизация работает только во время компиляции, и чтобы определить какое именно из значений в итоге будет у поля, и, соответственно, какой тип, компилятору нужно выполнить всю программу. В общем случае это неразрешимая задача, и определение типа any отдается на откуп программисту.
В то же время, если убрать одно из полей в классе T1, например stringProp, компилятор может проверить, что у объекта передаваемого класса, отсутствуют обязательное поле и кинет ошибку. А также, если вместо any использовать конкретные типы, например public stringProp: number; компилятор опять подскажет, что типы не совпадают.
Примеры для проверки
Что касается второго примера:
interface INumberWithFuncProcessor { num: number; func(number): string; }
Ошибочно само описание интерфейса, и при выборе опции noImplicitAny компилятор подсказывает, что number - это параметр, и так как у него не указан напрямую тип, его тип будет any и как мы помним - any не проверяется в compile-time. Таким образом, вполне нормально передавать в него переменные любого типа.
Если же указать конкретный тип параметра, например:
interface INumberWithFuncProcessor { num: number; func(a:number): string; }
То компилятор вполне резонно покажет ошибку:
Argument of type 'INumberWithFuncProcessor' is not assignable to parameter of type 'number'
Пример для проверки

понедельник, 3 декабря 2018 г.

Angular 2 thinking and way

Ситуация: Есть два очень похожих компонента: превью статьи на главной странице, и сама статья, при переходе на "читать дальше". У компонента превью свой сервис, который тянет данные через http. В роутере, при переходе на полноую статью (другой компонент) указан компонент статьи, непосредственно.
Вопрос: Создавать копмоненту свой собственный сервис, который будет дублировать код сервиса превьюхи, либо же использовать один компонент, просто с отдельным шаблоном целой статьи (//не нашел, как юзать несколько шаблонов в одном компоненте), либо же ваш вариант. Интересует именно правильный подход с точки зрения angular way и TS. Заранее благодарю.


Ответ

Для обоих компонентов можно сделать через один сервис