typescript - Angular 4, Proprietatea 'XX' lipsește în tipul '{xx, xx}'
Sunt nou în acest tip de scripting. Iată clasa mea de model:
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;
}
}
Și, mai jos este inițializarea mea, la care se face referire în tutorialul Angular:
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") }
];
Se așteaptă ca modelul să fie utilizat astfel în șablon:
{{ project.getDaysRemaining() }}
Dar, primesc eroarea:
Proprietatea 'getDaysRemaining' lipsește în tipul '{ projectId: number; description: string; startDate: Date; endDate: Date; }'.
De ce trebuie să inițializez funcția în TypeScript? Sau, îmi scapă ceva?
Răspuns #1
Problema ta are legătură cu modul în care este instanțiată o clasă.
În loc de:
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") }
];
ar trebui să faci:
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")
];
Explicație
Project este o clasă. getDaysRemaining() este un membru al acelei clase. Pentru a crea o instanță a unei clase, poți folosi new Project(...).
{ projectId: 1, ... } este un obiect care nu este o instanță a clasei Project. Prin urmare, toate elementele sunt definite între paranteze ca proprietăți.
Poți verifica dacă { projectId: 1, ... } este o instanță a Project pur și simplu prin { project1: 1, ... } instanceof Project - rezultând false.
ES6? TypeScript are mare parte din sintaxă în comun cu cele mai recente îmbunătățiri ale limbajului JavaScript. Poți defini clase în ES6 similar modului în care o faci în TypeScript. Desigur, cu excepția tipurilor și a modificatorilor, deoarece JavaScript nu este un limbaj cu tipare statice, ci unul cu tipare dinamice.
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;
}
}
În ES6 poți face new Project(), rezultând undefined pentru toate argumentele, dar în TypeScript nu poți pentru că lipsesc argumentele constructorului, iar acest fapt este verificat la compilare, și dacă există un IDE bun care verifică sintaxa TypeScript, atunci și la timpul de proiectare.
O opțiune pentru a permite acest lucru în TypeScript este să definești argumente opționale astfel (vezi semnul întrebării lângă argument):
constructor(
public projectId?: number,
public description?: string,
public startDate?: Date,
public endDate?: Date
) { }
Problema comună - obțineți unele date dintr-o sursă, cum ar fi un serviciu web
S-ar putea să întâlniți situația în care primiți datele Project de la un serviciu web. În acest caz, datele vor fi deserializate în obiecte simple { projectId: 1, ... }. Obținerea instanțelor Project din acele date nu se poate face implicit și este nevoie de muncă suplimentară.
Cel mai simplu este să transferi fiecare proprietate de la o parte la alta într-o metodă de fabricație:
class Project implements IProject {
...
static create(data: IProject) {
return new Project(data.projectId, data.description, data.startDate, data.endDate);
}
...
}
și poți folosi o interfață (în loc de any) pentru datele tale pentru a oferi ajutor suplimentar în timpul tastării, și de asemenea implementând-o (class Project implements IProject):
interface IProject {
projectId: number;
description: string;
startDate: Date;
endDate: Date;
}
O altă opțiune este să folosiți argumente opționale, dar acest lucru s-ar putea să nu se aplice în toate cazurile:
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);
}
...
}
astfel interfața ar avea argumente opționale (verifică semnul întrebării) dacă urmează să fie implementată ca mai sus:
interface IProject {
projectId?: number;
description?: string;
startDate?: Date;
endDate?: Date;
}
Compilare și interfețe în timp ce TypeScript este transpus în JavaScript, toate interfețele vor fi eliminate din codul final. Nu există conceptul de „interfață” în JavaScript. Acesta este un construct al TypeScript a cărui scop este de a oferi informații suplimentare despre tipuri.
încearcă să fii SOLID - S=Principiul Responsabilității Unice
În codul de mai sus Project are două responsabilități (nu una). Prima este să rețină unele date despre Project. A doua este să calculeze zilele rămase - getDaysRemaining. Aceasta ar putea fi prea mult pentru o singură clasă.
O soluție simplă ar putea fi să împărțim asta în două.
Creează o interfață pentru a reprezenta datele Project:
interface IProject {
projectId: number;
description: string;
startDate: Date;
endDate: Date;
}
Și mută getDaysRemaining() într-o altă clasă, astfel:
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;
}
}
Acest lucru are avantajul că datele - IProject - sunt separate și funcționalități suplimentare pot fi plasate în alte clase care își fac propria activitate - de exemplu - ProjectSchedulingService.
Un alt avantaj este că IProject poate ajuta la lucrul cu obiecte simple în loc să instanțiezi clase suplimentare. Poți obține date de la un serviciu web, le poți converti la o interfață și să lucrezi într-un mod tipizat.
Un alt avantaj este că afacerea poate fi împărțită în mai multe clase - cum ar fi ProjectSchedulingService - permițând astfel testarea unitară individuală în loc de a avea o clasă monolitică grea cu datele și toată logica de afaceri aferentă.
Un alt avantaj este că având mai multe clase ajută la definirea dependențelor între clase mai ușor, păstrând astfel semantica afacerii, și permite simularea tuturor dependențelor unei clase și testarea unităților logicii afacerii clasei respective.
Luați Angular ca exemplu, puteți avea mai multe servicii (clase) care pot fi injectate după cum este necesar. A avea un singur serviciu monolitic ar însemna să treceți ceva din care doar o parte este necesară. Astfel, se încalcă și separarea interfeței.
Acest lucru ar putea părea prostesc la început, deoarece codul este atât de mic, dar pe măsură ce timpul trece, acesta va continua să crească și dezvoltatorii viitori ar putea continua să dezvolte în același mod monolitic în loc să refactureze. După o anumită perioadă, multe estimări vor crește sau vor fi greșite din cauza dificultății de a întreține codul. De asemenea, complexitatea ciclotomică se va înrăutăți și acoperirea codului pentru testele unitare va scădea.
