typescript - Evitarea duplicării declarațiilor câmpurilor în definiția interfeței și a clasei
Am o interfață ca
interface I {
a : string;
b : number;
c : string;
}
Și acum declar o clasă care va avea membri publici a, b, c conform interfeței I.
Așadar, declar
class C implements I {
public a : string;
public b : number;
public c : string;
// and then constructor, other fields, other methods
}
Există vreo modalitate pentru mine de a evita duplicarea la declararea câmpurilor? Adică, vreau să am acele câmpuri fără a le declara efectiv, prin simpla extindere a interfeței. Așadar, tot ce va trebui să scriu ar fi ceva de genul:
class C implements I {
// perhaps a magic statement that asserts that C should inherit all members from I
// and then constructor, other fields, other methods
}
Ca să fie totul și mai rău, există și mai multă duplicare în constructor deoarece nu pot pur și simplu să inițializez câmpurile dintr-un literal de obiect care satisface interfața I într-o singură linie
constructor(data : I) {
this.a = data.a;
this.b = data.b;
this.c = data.c;
}
Nu am control asupra definiției interfeței, am control doar asupra definiției clasei (aceasta înseamnă că, de exemplu, nu pot transforma interfața într-o clasă abstractă...)
Răspuns #1
DA, este posibil. Necesită un cod puțin neintuitiv, dar va asigura corect că nu adaugi cod redundant.
Aveți nevoie de următoarea linie în afara definiției clasei:
interface I {
a : string;
b : number;
c : string;
}
interface C extends I {}; //<--- this line
class C implements I {
constructor(data : I) {
this.a = data.a;
this.b = data.b;
this.c = data.c;
}
}
Link către terenul de joacă
Acum compilatorul TypeScript va considera că clasa C a declarat aceleași câmpuri ca interfața I.
Funcționează astfel:
interface Cdeclară o interfață numităC.- `
extends I` înseamnă că moștenește proprietățile din interfața existentă `I`. {}este doar corpul acestei interfețeC- este gol, deoarece nu adăugăm sau schimbăm nimic.
De unul singur, nu este foarte impresionant. Totuși, există deja o clasă numită C, așadar interfața cu același nume se referă la aceeași interfață pe care o are clasa. În termeni simpli,
class A {
public foo: string;
constructor(data: string) {
this.foo = data;
}
}
compilează în JavaScript (țintă ES6+) către
class A {
constructor(data) {
this.foo = data;
}
}
informațiile eliminate se referă la interfața clasei pe care TypeScript o va folosi la timpul de compilare pentru a se asigura că folosești corect clasa. De exemplu:
- adăugarea sau referirea la proprietăți care nu sunt declarate
- utilizând proprietățile declarate (cum ar fi
foo: string) conform tipului lor (de exemplu, fără a tratafooca pe un număr). - apelarea constructorului cu numărul și tipul corect de argumente
Declararea unei interfețe cu același nume ca o clasă va combina în schimb declarația cu cea a clasei. Aceasta este motivul pentru care interfața clasei poate fi manipulată. Și în acest caz, folosim extends (care este valid pentru interfețe) pentru a îmbogăți definiția clasei (unde este permis doar implements).
Ca referință, am preluat tehnica din acest comentariu într-un fir de discuție despre probleme din depozitul TypeScript pe GitHub. Titlul problemei de pe GitHub este „Declarațiile claselor ar trebui să implementeze implicit interfețele” iar comentariul este de la RyanCavanaugh din 17 februarie 2016:
Observați că în cea mai recentă versiune de TypeScript, puteți folosi combinarea de clase/interfețe pentru a face acest lucru:
interface Foo { a: number; } interface Baz extends Foo { } class Baz { constructor() { console.log(this.a); // no error here } }mulțumiri @jeffreymorlan pentru că mi-a reamintit de asta
[Notă: cea mai recentă versiune lansată la momentul comentariului ar fi fost 1.8 Beta]
