2017-05-12 2 views
2

Ich bin neu in dieser Art von Typ-Scripting. Hier ist meine Modellklasse:Angular 4, Objekt 'XX' befindet sich in Typ fehlt '{xx, xx}'

export class Project { 
    constructor(
      public projectId: number, 
      public description: string, 
      public startDate: Date, 
      public endDate: Date 
    ) {} 
    getDaysRemaining() { 
      var result = this.endDate.valueOf() - Date.now().valueOf(); 
      result = Math.ceil(result/(1000 * 3600 * 24)); 
      return result < 0 ? 0 : result; 
    } 
} 

Und unten ist meine Initialisierungseinrichtung, um Angular Tutorial bezeichnet:

let PROJECTS: Project[] = [ 
    { projectId: 1, description: "Sample", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") }, 
    { projectId: 2, description: "Sample 2", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") } 
]; 

Das Modell erwartet wird, wie dies in Vorlage verwendet werden sollte:

{{ project.getDaysRemaining() }} 

Aber ich erhalte den Fehler:

Property 'getDaysRemaining' is missing in type '{ projectId: number; description: string; startDate: Date; endDate: Date; }'.

Warum muss ich die Funktion in TypeScript initialisieren? Oder verpasse ich etwas?

+0

Dann gibt es keine direkte Methode zum Anzeigen des Ergebnisses einer Funktion in der Vorlage –

Antwort

7

Ihr Problem hat damit zu tun, wie eine Klasse instanziiert wird.

Statt:

let PROJECTS: Project[] = [ 
    { projectId: 1, description: "Sample", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") }, 
    { projectId: 2, description: "Sample 2", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") } 
]; 

Sie tun sollten:

let PROJECTS: Project[] = [ 
    new Project(1, "Sample", new Date("2016-12-12"), new Date("2017-1-13")), 
    new Project(2, "Sample 2", new Date("2016-12-12"), new Date("2017-1-13") 
]; 

Erklärung

Project ist eine Klasse. getDaysRemaining() ist ein Mitglied dieser Klasse. Um eine Instanz einer Klasse zu erstellen, können Sie new Project(...) verwenden.

{ projectId: 1, ... } ist ein Objekt, das nicht eine Instanz der Klasse Project ist. Daher sind alle Elemente zwischen den Klammern als Eigenschaften definiert.

Sie können überprüfen, ob eine Instanz von Project ist, einfach { project1: 1, ... } instanceof Project - Ergebnis false.

ES6? TypeScript hat viel von der Syntax mit den neuesten Fortschritten in JavaScript Sprache. Sie können Klassen in ES6 ähnlich wie in TypeScript definieren. Abgesehen von den Typen und Modifizierern ist JavaScript natürlich keine statisch typisierte, sondern eine dynamisch typisierte Sprache.

class Project { 

    constructor(projectId, description, startDate, endDate) { 
     this.projectId = projectId; 
     this.description = description; 
     this.startDate = startDate; 
     this.endDate = endDate; 
    } 

    getDaysRemaining() { 
      var result = this.endDate.valueOf() - Date.now().valueOf(); 
      result = Math.ceil(result/(1000 * 3600 * 24)); 
      return result < 0 ? 0 : result; 
    } 
} 

In ES6 Sie new Project() tun können, undefined für alle Argumente nachgebend, aber in Typoskript nicht kann, weil es fehlen Konstruktorargumente, und diese Tatsache wird bei der Kompilierung überprüft, und wenn es eine gute IDE, die Typoskript überprüft Syntax, dann auch zur Entwurfszeit.

Eine Möglichkeit dies in Typoskript zu ermöglichen, ist optional Argumente wie so zu definieren (siehe das Fragezeichen neben dem Argumente):

Allgemeines Problem - einige Daten aus einer Quelle erhalten, wie eine Bahn

Service

Sie könnten den Fall auftreten, wenn Sie Ihre Project Daten von einem Web-Service. In diesem Fall werden die Daten in einfache Objekte deserialisiert { projectId: 1, ... }.Das Abrufen von Project Instanzen aus diesen Daten kann nicht implizit durchgeführt werden und zusätzliche Arbeiten müssen durchgeführt werden.

Die einfachste ist vorbei jede Eigenschaft von einer Seite auf die andere in einem Factory-Methode:

class Project implements IProject { 
    ... 
    static create(data: IProject) { 
     return new Project(data.projectId, data.description, data.startDate, data.endDate); 
    } 
    ... 
} 

und Sie können eine Schnittstelle (statt any) für Ihre Daten verwenden, um zusätzliche Hilfe zur Verfügung zu stellen, während Sie tippen, und auch deren Umsetzung (class Project implements IProject):

interface IProject { 
    projectId: number; 
    description: string; 
    startDate: Date; 
    endDate: Date; 
} 

Eine weitere Option ist optional Argumente zu verwenden, aber dies möglicherweise nicht für alle Fälle:

class Project implements IProject { 
    ... 
    constructor(
      public projectId?: number, 
      public description?: string, 
      public startDate?: Date, 
      public endDate?: Date 
    ) { } 

    static create(data: IProject) { 
      return Object.assign(new Project(), data); 
    } 
    ... 
} 

so würde die Schnittstelle optionale Argumente haben (das Fragezeichen prüfen), wenn wie oben umgesetzt werden soll:

interface IProject { 
    projectId?: number; 
    description?: string; 
    startDate?: Date; 
    endDate?: Date; 
} 

Compilation und Schnittstellen während das Typoskript in JavaScript alle Schnittstellen transpiled Materialen werden aus dem endgültigen Code entfernt. Es gibt kein "Interface" -Konzept in JavaScript. Dies ist ein Konstrukt von TypeScript, das dazu dient, zusätzliche Typinformationen bereitzustellen. S = Einzel Prinzip Verantwortung

In dem obigen Code Project hat zwei Aufgaben (nicht ein) -

try SOLID zu sein. 1. ist, einige Project Daten zu halten. 2. ist die verbleibenden Tage zu berechnen - getDaysRemaining. Dies könnte für eine einzelne Klasse zu viel sein.

Eine saubere Lösung könnte sein, dieses in zwei Teile zu teilen.

Haben sie eine Schnittstelle, die Daten des Project darzustellen:

interface IProject { 
    projectId: number; 
    description: string; 
    startDate: Date; 
    endDate: Date; 
} 

und bewegen Sie die getDaysRemaining() in eine andere Klasse wie:

class ProjectSchedulingService { 
    getDaysRemaining(project: IProject) { 
     var result = project.endDate.valueOf() - Date.now().valueOf(); 
     result = Math.ceil(result/(1000 * 3600 * 24)); 
     return result < 0 ? 0 : result; 
    } 
} 

Dies hat den Vorteil, dass die die Daten - IProject - ist separate und zusätzliche Funktionalität können in anderen Klassen platziert werden, die ihre eigenen Aufgaben erledigen - wie zB - ProjectSchedulingService.

Ein weiterer Vorteil ist, dass IProject helfen kann, mit einfachen Objekten zu arbeiten, anstatt zusätzliche Klassen zu instanziieren. Sie können Daten von einem Webdienst abrufen, sie an eine Schnittstelle übergeben und typisiert arbeiten. Ein weiterer Vorteil ist, dass das Geschäft in mehrere Klassen aufgeteilt werden kann - wie ProjectSchedulingService - und somit in der Lage ist, einzeln getestet zu werden, anstatt eine schwere monolithische Klasse mit Daten und allen damit verbundenen Geschäften zu haben. Ein weiterer Vorteil besteht darin, dass das Vorhandensein mehrerer Klassen dazu beiträgt, Abhängigkeiten zwischen Klassen leichter zu definieren, so dass Geschäftssemantik erhalten bleibt und alle Abhängigkeiten einer Klasse und Einheit simuliert werden können, um das Geschäft der Klasse zu testen.

Nehmen Sie beispielsweise Angular, können Sie mehrere Dienste (Klassen) haben, die nach Bedarf injiziert werden können.Einen monolithischen Dienst zu haben, würde bedeuten, etwas zu übergeben, von dem nur ein Teil davon benötigt wird. Somit bricht auch die Schnittstellentrennung.

Das hört sich vielleicht zuerst albern an, da der Code so klein ist, aber im Laufe der Zeit wird dies weiter wachsen und die nächsten Entwickler können sich immer noch auf die gleiche monolithische Art weiterentwickeln anstatt zu refactoring. Nach einiger Zeit werden viele Schätzungen aufgrund der Schwierigkeit, den Code beizubehalten, erhöht oder unterbrochen. Auch cyclomatic complexity wird am schlechtesten und auch die Codeabdeckung für Komponententests.

+0

Vielen Dank. Es ist 2017 und diese ausführliche Antwort ist immer noch sehr hilfreich. – Emaro

Verwandte Themen