Supraîncărcarea constructorului în TypeScript (Programare, Typescript, Constructor, Supraîncărcare)

Ted a intrebat.

A făcut cineva supraîncărcarea constructorului în TypeScript. La pagina 64 din specificația limbajului (v 0.8), există declarații care descriu supraîncărcarea constructorilor, dar nu a fost dat niciun exemplu de cod.

Acum încerc o declarație de clasă foarte elementară; arată astfel,

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor(obj: IBox) {    
        this.x = obj.x;
        this.y = obj.y;
        this.height = obj.height;
        this.width = obj.width;
    }   

    constructor() {
        this.x = 0;
        this.y = 0;
        this.width = 0;
        this.height = 0;
    }
}

Când este rulat cu tsc BoxSample.ts, acesta aruncă o definiție duplicată a constructorului – ceea ce este evident. Orice ajutor este apreciat.

Comentarii

  • Din câte îmi dau seama, nu suportă încă constructori multipli.  > Por Johan.
  • încă nu suportă constructori multipli. Tocmai am încercat 🙁 –  > Por Asma Rahim Ali Jafri.
  • Verificați acest răspuns: stackoverflow.com/a/58788876/2746447, declarați câmpurile clasei doar o singură dată –  > Por egor.xyz.
16 răspunsuri
chuckj

TypeScript vă permite să declarați supraîncărcări, dar nu puteți avea decât o singură implementare, iar acea implementare trebuie să aibă o semnătură compatibilă cu toate supraîncărcările. În exemplul dvs., acest lucru se poate face cu ușurință cu un parametru opțional ca în,

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor(obj?: IBox) {    
        this.x = obj && obj.x || 0
        this.y = obj && obj.y || 0
        this.height = obj && obj.height || 0
        this.width = obj && obj.width || 0;
    }   
}

sau două supraîncărcări cu un constructor mai general ca în,

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor();
    constructor(obj: IBox); 
    constructor(obj?: any) {    
        this.x = obj && obj.x || 0
        this.y = obj && obj.y || 0
        this.height = obj && obj.height || 0
        this.width = obj && obj.width || 0;
    }   
}

Comentarii

  • De fapt, ar trebui să fie posibil să se permită compilatorului să genereze javascript pentru a determina în timpul execuției ce supraîncărcare a fost luată. Dar acest lucru este puțin probabil, deoarece filozofia lor pare să fie aceea de a genera cât mai puțin javascript posibil. –  > Por remcoder.
  • @remcoder, acest lucru ar fi întotdeauna adevărat. Unele tipuri de informații despre tipuri nu sunt disponibile la momentul execuției. De exemplu, nu există un concept de interfață IBox în JavaScript-ul generat. Ar putea funcționa pentru clase și tipuri încorporate, dar presupun că, având în vedere potențiala confuzie în jurul acestui aspect, a fost omis. –  > Por Drew Noakes.
  • O altă notă foarte importantă: în timp ce TypeScript nu este deja sigur din punct de vedere al tipurilor, acest lucru îl invadează și mai mult. Supraîncărcarea funcțiilor, așa cum se face aici, pierde orice proprietăți care pot fi verificate cu privire la funcție. Compilatorului nu-i va mai păsa și va presupune că tipurile returnate sunt corecte. –  > Por froginvasion.
  • Ce face ca acest lucru să nu fie sigur din punct de vedere al tipurilor? Ne asigurăm în continuare că tipul este number cu public x: number. Siguranța constă în faptul că ne asigurăm că parametrii, dacă sunt trecuți, sunt de un tip corect. –  > Por nikk wong.
  • @nikkwong punctul de vedere al lui froginvasion a fost că, folosind această tehnică, TypeScript nu verifică corectitudinea implementării supraîncărcate în ceea ce privește supraîncărcările. Locurile de apelare sunt verificate, dar nu și implementarea. Deși nu este „typesafe”, folosind definiția implicită a lui froginvasion, aceasta limitează codul care poate fi acuzat de erori de tip la implementarea supraîncărcată. –  > Por chuckj.
kbtz

În ceea ce privește supraîncărcările constructorilor o alternativă bună ar fi să se implementeze supraîncărcările suplimentare ca metode statice de fabrică. Cred că este mai ușor de citit și mai ușor decât să verifice toate combinațiile posibile de argumente la constructor.

În exemplul următor, putem crea un obiect pacient folosind date de la un furnizor de asigurări care stochează valorile în mod diferit. Pentru a susține încă o altă structură de date pentru instanțierea pacientului, s-ar putea adăuga pur și simplu o altă metodă statică pentru a apela constructorul implicit cât mai bine după normalizarea datelor furnizate.

class Patient {
    static fromInsurance({
        first, middle = '', last,
        birthday, gender
    }: InsuranceCustomer): Patient {
        return new this(
            `${last}, ${first} ${middle}`.trim(),
            utils.age(birthday),
            gender
        );
    }

    constructor(
        public name: string,
        public age: number,
        public gender?: string
    ) {}
}

interface InsuranceCustomer {
    first: string,
    middle?: string,
    last: string,
    birthday: string,
    gender: 'M' | 'F'
}


const utils = { /* included in the playground link below */};

{// Two ways of creating a Patient instance
    const
        jane = new Patient('Doe, Jane', 21),
        alsoJane = Patient.fromInsurance({ 
            first: 'Jane', last: 'Doe',
            birthday: 'Jan 1, 2000', gender: 'F'
        })

    console.clear()
    console.log(jane)
    console.log(alsoJane)
}

Puteți verifica rezultatul la TS Playground


Supraîncărcarea metodelor în TypeScript nu este reală, să spunem, deoarece ar necesita prea mult cod generat de compilator, iar TS este conceput pentru a evita acest lucru cu orice preț. Principalul caz de utilizare pentru supraîncărcarea metodelor este probabil scrierea declarațiilor pentru bibliotecile care au argumente magice în API-ul lor. Din moment ce tot greul de a gestiona diferite seturi de argumente posibile este făcut de dumneavoastră, nu văd prea multe avantaje în utilizarea supraîncărcărilor în loc de metode ad-hoc pentru fiecare scenariu.

Comentarii

  • ați putea folosi (data: Partial<PersonData>) dacă nu doriți să solicitați întotdeauna first, last, și birthday să fie prezente în data. –  > Por Ullauri.
  • De asemenea, modificatorul de acces al constructorului poate fi schimbat din public la private/protected, iar atunci singura modalitate de a crea un obiect sunt metodele statice de fabrică. Uneori, acest lucru poate fi foarte util. –  > Por Oleg Zarevennyi.
Benson

Se pare că doriți ca parametrul obiectului să fie opțional și, de asemenea, fiecare dintre proprietățile din obiect să fie opționale. În exemplul, așa cum este furnizat, nu este necesară sintaxa de supraîncărcare. Am vrut să subliniez unele practici greșite în unele dintre răspunsurile de aici. De acord, nu este cea mai mică expresie posibilă de a scrie în esență box = { x: 0, y: 87, width: 4, height: 0 }, dar aceasta oferă toate detaliile de indicii de cod pe care le-ați putea dori de la clasa descrisă. Acest exemplu vă permite să apelați o funcție cu unul, unii, toți, sau niciunul dintre parametri și să obțină în continuare valori implicite.

 /** @class */
 class Box {
     public x?: number;
     public y?: number;
     public height?: number;
     public width?: number;     

     constructor(params: Box = {} as Box) {

         // Define the properties of the incoming `params` object here. 
         // Setting a default value with the `= 0` syntax is optional for each parameter
         let {
             x = 0,
             y = 0,
             height = 1,
             width = 1
         } = params;
         
         //  If needed, make the parameters publicly accessible
         //  on the class ex.: 'this.var = var'.
         /**  Use jsdoc comments here for inline ide auto-documentation */
         this.x = x;
         this.y = y;
         this.height = height;
         this.width = width;
     }
 }

Aveți nevoie să adăugați metode? O alternativă mai verboasă, dar mai ușor de extins: Box clasa de mai sus poate funcționa ca interfață, deoarece sunt identice. Dacă alegeți să modificați clasa de mai sus, va trebui să definiți și să faceți referire la o nouă interfață pentru obiectul parametrilor de intrare, deoarece clasa Box nu va mai arăta exact ca parametrii de intrare. Observați unde se află semnele de întrebare (?:) care denotă proprietățile opționale se mută în acest caz. Deoarece setează valori implicite în cadrul clasei, acestea sunt garantate a fi prezente, dar sunt opționale în cadrul obiectului parametrilor de intrare:

    interface BoxParams {
        x?: number;
         // Add Parameters ...
    }

    class Box {
         public x: number;
         // Copy Parameters ...
         constructor(params: BoxParams = {} as BoxParams) {
         let { x = 0 } = params;
         this.x = x;
    }
    doSomething = () => {
        return this.x + this.x;
        }
    }

Oricare ar fi modul în care alegeți să vă definiți clasa, această tehnică oferă siguranța tipului, dar și flexibilitatea de a scrie oricare dintre acestea:

const box1 = new Box();
const box2 = new Box({});
const box3 = new Box({x:0});
const box4 = new Box({x:0, height:10});
const box5 = new Box({x:0, y:87,width:4,height:0});

 // Correctly reports error in TypeScript, and in js, box6.z is undefined
const box6 = new Box({z:0});  

Compilate, vedeți cum setările implicite sunt folosite doar dacă o valoare opțională este nedefinită; aceasta evită capcanele unei sintaxe de rezervă foarte utilizate (dar predispuse la erori) de tipul var = isOptional || default; prin verificarea cu void 0, care este o prescurtare pentru undefined:

Ieșirea compilată

var Box = (function () {
    function Box(params) {
        if (params === void 0) { params = {}; }
        var _a = params.x, x = _a === void 0 ? 0 : _a, _b = params.y, y = _b === void 0 ? 0 : _b, _c = params.height, height = _c === void 0 ? 1 : _c, _d = params.width, width = _d === void 0 ? 1 : _d;
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
    }
    return Box;
}());

Addendum: Setarea valorilor implicite: în mod greșit

Site-ul || (sau) operator

Luați în considerare pericolul ||operatorilor /or atunci când setați valori de rezervă implicite, așa cum se arată în alte răspunsuri. Acest cod de mai jos ilustrează modul greșit de a seta valori implicite. Puteți obține rezultate neașteptate atunci când evaluați cu falsey valori precum 0, ”, null, undefined, false, NaN:

var myDesiredValue = 0;
var result = myDesiredValue || 2;

// This test will correctly report a problem with this setup.
console.assert(myDesiredValue === result && result === 0, 'Result should equal myDesiredValue. ' + myDesiredValue + ' does not equal ' + result);

Object.assign(this,params)

În testele mele, folosind obiectul destructurat es6/typescript destructured object poate fi cu 15-90% mai rapidă decât Object.assign. Utilizarea unui parametru destructurat permite doar metodele și proprietățile pe care le-ați atribuit obiectului. De exemplu, luați în considerare această metodă:

class BoxTest {
    public x?: number = 1;

    constructor(params: BoxTest = {} as BoxTest) {
        Object.assign(this, params);
    }
}

Dacă un alt utilizator nu folosea TypeScript și încerca să plaseze un parametru care nu îi aparține, să zicem, ar putea încerca să pună un parametru z proprietate

var box = new BoxTest({x: 0, y: 87, width: 4, height: 0, z: 7});

// This test will correctly report an error with this setup. `z` was defined even though `z` is not an allowed property of params.
console.assert(typeof box.z === 'undefined')

Comentarii

  • Știu că este un fir de discuție cam vechi, dar castingul Ibox mi-a rupt mințile, puteți să-mi explicați cum funcționează? –  > Por Nickso.
  • Mi-am actualizat răspunsul pentru a elimina distribuția superfluă care a fost o reportare de la codificarea pentru Typescript 1.8. Castingul care rămâne este pentru obiectul gol ( {} devine obiectul implicit dacă nu sunt definiți parametri; și din moment ce {} nu este validat ca o cutie, îl transformăm în cutie. Această distribuție ne permite să creăm o nouă cutie fără niciun parametru definit. În IDE-ul dumneavoastră, puteți introduce exemplul meu, precum și const box1 = new Box(); și puteți vedea cum castingul rezolvă unele dintre mesajele de eroare pe care le vedem în scenariile de utilizare. –  > Por Benson.
  • @Benson exemplul BoxTest conține erori. Compilatorul TypeScript se plânge corect de utilizarea greșită a constructorului, dar atribuirea va avea loc în continuare. Afirmația eșuează deoarece box.z este de fapt 7 în codul dumneavoastră, nu undefined. –  > Por Steven Liekens.
  • Am adăugat o metodă la clasa Box și apoi constructorul nu a mai funcționat (a eșuat în timpul compilării). Aveți vreo idee? –  > Por JeeShen Lee.
  • @JeeShenLee ați putea fie să extindeți clasa Box la o clasă cu nume nou, cu metode, fie să creați o interfață pentru parametrii așteptați. Tipul de interfață este împrumutat de la clasa Box, deoarece clasele pot acționa ca interfețe. Cu metoda adăugată de dvs., interfața se aștepta ca o metodă să fie trecută ca parte a obiectului, deoarece clasa are dublă funcție de interfață. Copiați primele cinci linii ale clasei Box și schimbați-o într-o interfață cu un nume nou, cum ar fi interface BoxConfig { x?: number ...} și apoi modificați linia constructor(obj: BoxConfig = {} as BoxConfig) { –  > Por Benson.
ShinNoNoir

Rețineți că puteți, de asemenea, să ocoliți lipsa de supraîncărcare la nivel de implementare prin intermediul parametrilor impliciți în TypeScript, de ex:

interface IBox {    
    x : number;
    y : number;
    height : number;
    width : number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor(obj : IBox = {x:0,y:0, height:0, width:0}) {    
        this.x = obj.x;
        this.y = obj.y;
        this.height = obj.height;
        this.width = obj.width;
    }   
}

Edit:Începând cu 5 dec. ’16, consultați răspunsul lui Benson pentru o soluție mai elaborată care permite mai multă flexibilitate.

Comentarii

  • Ce se întâmplă cu interface IBox extends Box? –  > Por Ahmad.
vegemite4me

Actualizare 2 (28 septembrie 2020): Acest limbaj este în continuă evoluție, așa că, dacă puteți utiliza Partial (introdusă în v2.1), atunci aceasta este acum modalitatea mea preferată pentru a realiza acest lucru.

class Box {
   x: number;
   y: number;
   height: number;
   width: number;

   public constructor(b: Partial<Box> = {}) {
      Object.assign(this, b);
   }
}

// Example use
const a = new Box();
const b = new Box({x: 10, height: 99});
const c = new Box({foo: 10});          // Will fail to compile

Actualizare (8 iunie 2017): guyarad și snolflake fac puncte valide în comentariile lor de mai jos la răspunsul meu. Aș recomanda cititorilor să se uite la răspunsurile lui Benson, Joe și snolflake, care au răspunsuri mai bune decât ale mele.*

Răspunsul original (27 ianuarie 2014)

Un alt exemplu de realizare a supraîncărcării constructorilor:

class DateHour {

  private date: Date;
  private relativeHour: number;

  constructor(year: number, month: number, day: number, relativeHour: number);
  constructor(date: Date, relativeHour: number);
  constructor(dateOrYear: any, monthOrRelativeHour: number, day?: number, relativeHour?: number) {
    if (typeof dateOrYear === "number") {
      this.date = new Date(dateOrYear, monthOrRelativeHour, day);
      this.relativeHour = relativeHour;
    } else {
      var date = <Date> dateOrYear;
      this.date = new Date(date.getFullYear(), date.getMonth(), date.getDate());
      this.relativeHour = monthOrRelativeHour;
    }
  }
}

Sursă: http://mimosite.com/blog/post/2013/04/08/Overloading-in-TypeScript

Comentarii

    23

  • Acesta nu este un comentariu constructiv – dar, wow, este urât. Pierde din vedere ideea de tipul în TypeScript… –  > Por Guy.
  • Este o supraîncărcare a constructorului?! Nu, mulțumesc! Aș prefera să implementez o metodă statică de fabrică pentru acea clasă, într-adevăr foarte urât. –  > Por kbtz.
  • Presupun că astăzi am putea avea dateOrYear: Data | număr, –  > Por Pascal Ganaye.
Joe

Știu că este o întrebare veche, dar o noutate în 1.4 sunt tipurile union; folosiți-le pentru toate supraîncărcările de funcții (inclusiv pentru constructori). Exemplu:

class foo {
    private _name: any;
    constructor(name: string | number) {
        this._name = name;
    }
}
var f1 = new foo("bar");
var f2 = new foo(1);

Comentarii

  • Nu ar trebui să fie name să fie și el de tip string | number în loc de any? –  > Por Carcigenicate.
  • Cu siguranță ai putea face asta, da, și ar putea fi un pic mai consistent, dar în acest exemplu, ți-ar da acces doar la .toString() și .valueOf(), în Intellisense, așa că, pentru mine, folosind any este foarte bine, dar pentru fiecare în parte. –  > Por Joe.
Kostas Drak

De fapt, s-ar putea să fie prea târziu pentru acest răspuns, dar acum puteți face acest lucru:

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor();
    constructor(obj: IBox);
    constructor(obj?: IBox) {    
        this.x = !obj ? 0 : obj.x;
        this.y = !obj ? 0 : obj.y;
        this.height = !obj ? 0 : obj.height;
        this.width = !obj ? 0 : obj.width;
    }
}

deci, în loc de metode statice, puteți utiliza metodele de mai sus. Sper că vă va fi de ajutor!!!

Comentarii

  • Minunat! Trebuie să aveți în vedere aici că fiecare câmp suplimentar nou al altor constructori ar trebui marcat ca fiind opțional; așa cum ați făcut deja pentru obj? –  > Por Radu Linu.
  • Al doilea constructor nu este constructor(obj: IBox); redundant? Ultimul nu se ocupă de aceste două cazuri? –  > Por zaplec.
Yacine

Vă puteți ocupa de acest lucru prin :

class Box {
  x: number;
  y: number;
  height: number;
  width: number;
  constructor(obj?: Partial<Box>) {    
     assign(this, obj);
  }
}

Partial va face câmpurile (x,y, înălțime, lățime) opționale, permițând mai mulți constructori

de exemplu: puteți face new Box({x,y}) fără înălțime și lățime.

Comentarii

  • Cred că mai trebuie să vă ocupați de valorile implicite pentru elementele lipsă. Ușor de făcut, totuși. –  > Por Charlie Reitzel.
  • sau constructor(obj?: Partial<Box>) +1 –  > Por naveen.
  • Parțiale sunt un răspuns excelent, dar de ce să introducem lowdash? –  > Por vegemite4me.
  • @vegemite4me ai dreptate, nu este nevoie de lodash. Object.assign este suficient –  > Por Yacine.
  • Atenție, această soluție încalcă contractul de clasă, deoarece Box definește că toate proprietățile sunt obligatorii, în timp ce această soluție permite ca ele să fie nedefinite. –  > Por DarkNeuron.
Mudlabs

Box încearcă să definească mai multe implementări de constructori.

Numai ultimul constructor semnătura de supraîncărcare este utilizată ca și semnătura clasei de constructor al clasei.

În exemplul de mai jos, observați că implementarea constructorului este definită astfel încât să facă nu este definită nu contrazice niciuna dintre cele precedente semnături de supraîncărcare.

interface IBox = {
    x: number;
    y: number;
    width: number;
    height: number;
}

class Box {
    public x: number;
    public y: number;
    public width: number;
    public height: number;

    constructor() /* Overload Signature */
    constructor(obj: IBox) /* Overload Signature */
    constructor(obj?: IBox) /* Implementation Constructor */ {
        if (obj) {
            this.x = obj.x;
            this.y = obj.y;
            this.width = obj.width;
            this.height = obj.height;
        } else {
            this.x = 0;
            this.y = 0;
            this.width = 0;
            this.height = 0
        }
    }

    get frame(): string {
        console.log(this.x, this.y, this.width, this.height);
    }
}

new Box().frame; // 0 0 0 0
new Box({ x:10, y:10, width: 70, height: 120 }).frame; // 10 10 70 120



// You could also write the Box class like so;
class Box {
    public x: number = 0;
    public y: number = 0;
    public width: number = 0;
    public height: number = 0;

    constructor() /* Overload Signature */
    constructor(obj: IBox) /* Overload Signature */
    constructor(obj?: IBox) /* Implementation Constructor */ {
        if (obj) {
            this.x = obj.x;
            this.y = obj.y;
            this.width = obj.width;
            this.height = obj.height;
        }
    }

    get frame(): string { ... }
}

parlament

În cazul în care un parametru opțional și tipizat este suficient de bun, luați în considerare următorul cod care realizează același lucru fără a repeta proprietățile sau a defini o interfață:

export class Track {
   public title: string;
   public artist: string;
   public lyrics: string;

   constructor(track?: Track) {
     Object.assign(this, track);
   }
}

Rețineți că acest lucru va atribui toate proprietățile trecute în track, chiar dacă acestea nu sunt definite pe Track.

Ardeshir Valipoor
interface IBox {
    x: number;
    y: number;
    height: number;
    width: number;
}

class Box {
    public x: number;
    public y: number;
    public height: number;
    public width: number;

    constructor(obj: IBox) {
        const { x, y, height, width } = { x: 0, y: 0, height: 0, width: 0, ...obj }
        this.x = x;
        this.y = y;
        this.height = height;
        this.width = width;
    }
}

Comentarii

  • În acest caz, nu ar fi mai bine să tastați parametrul ca fiind {} în loc de IBox? Deja enumerați constrângerile de proprietate… –  > Por Roy Tinker.
  • @RoyTinker da, aveți dreptate. Practic, răspunsul era greșit și l-am actualizat. –  > Por Ardeshir Valipoor.
miguel savignano

Putem simula supraîncărcarea constructorului folosind guards

interface IUser {
  name: string;
  lastName: string;
}

interface IUserRaw {
  UserName: string;
  UserLastName: string;
}

function isUserRaw(user): user is IUserRaw {
  return !!(user.UserName && user.UserLastName);
}

class User {
  name: string;
  lastName: string;

  constructor(data: IUser | IUserRaw) {
    if (isUserRaw(data)) {
      this.name = data.UserName;
      this.lastName = data.UserLastName;
    } else {
      this.name = data.name;
      this.lastName = data.lastName;
    }
  }
}

const user  = new User({ name: "Jhon", lastName: "Doe" })
const user2 = new User({ UserName: "Jhon", UserLastName: "Doe" })

ynotu.

Eu folosesc următoarea alternativă pentru a obține parametrii impliciți/opționali și constructori „de tip supraîncărcat” cu număr variabil de parametri:

private x?: number;
private y?: number;

constructor({x = 10, y}: {x?: number, y?: number}) {
 this.x = x;
 this.y = y;
}

Știu că nu este cel mai frumos cod, dar ne obișnuim cu el. Nu este nevoie de o interfață suplimentară și permite membri privați, ceea ce nu este posibil atunci când se utilizează interfața.

Radu Linu

Iată un exemplu funcțional și trebuie să aveți în vedere că fiecare constructor cu mai multe câmpuri trebuie să marcheze câmpurile suplimentare ca fiind optional.

class LocalError {
  message?: string;
  status?: string;
  details?: Map<string, string>;

  constructor(message: string);
  constructor(message?: string, status?: string);
  constructor(message?: string, status?: string, details?: Map<string, string>) {
    this.message = message;
    this.status = status;
    this.details = details;
  }
}

rustyBucketBay

Așa cum am comentat în răspunsul lui @Benson, am folosit acest exemplu în codul meu și l-am găsit foarte util. Cu toate acestea, am constatat că în cazul Object is possibly 'undefined'.ts(2532) eroare atunci când am încercat să fac calcule cu tipurile variabilelor din clasa mea, deoarece semnul de întrebare le conduce să fie de tip AssignedType | undefined. Chiar dacă cazul nedefinit este tratat în execuția ulterioară sau cu ajutorul compilatorului de tip enforce <AssignedType> nu am putut scăpa de eroare, așa că nu am putut face ca argetele să fie opționale. am rezolvat creând un tip separat pentru argumentele cu semnul de întrebare params și variabilele de clasă fără semne de întrebare. Verbose, dar a funcționat.

Iată codul original, care dădea eroarea în metoda class(), vezi mai jos:

/** @class */

class Box {
  public x?: number;
  public y?: number;
  public height?: number;
  public width?: number;

  // The Box class can work double-duty as the interface here since they are identical
  // If you choose to add methods or modify this class, you will need to
  // define and reference a new interface for the incoming parameters object 
  // e.g.:  `constructor(params: BoxObjI = {} as BoxObjI)` 
  constructor(params: Box = {} as Box) {
    // Define the properties of the incoming `params` object here. 
    // Setting a default value with the `= 0` syntax is optional for each parameter
    const {
      x = 0,
      y = 0,
      height = 1,
      width = 1,
    } = params;

    //  If needed, make the parameters publicly accessible
    //  on the class ex.: 'this.var = var'.
    /**  Use jsdoc comments here for inline ide auto-documentation */
    this.x = x;
    this.y = y;
    this.height = height;
    this.width = width;
  }

  method(): void {
    const total = this.x + 1; // ERROR. Object is possibly 'undefined'.ts(2532)
  }
}

const box1 = new Box();
const box2 = new Box({});
const box3 = new Box({ x: 0 });
const box4 = new Box({ x: 0, height: 10 });
const box5 = new Box({ x: 0, y: 87, width: 4, height: 0 });

Deci, variabila nu poate fi folosită în metodele clasei. dacă se corectează astfel, de exemplu, așa:

method(): void {
    const total = <number> this.x + 1;
}

Acum apare această eroare:

Argument of type '{ x: number; y: number; width: number; height: number; }' is not 
assignable to parameter of type 'Box'.
Property 'method' is missing in type '{ x: number; y: number; width: number; height: 
number; }' but required in type 'Box'.ts(2345)

Ca și cum întregul pachet de argumente nu ar mai fi opțional.

Deci, dacă se creează un tip cu argumente opționale, iar variabilele de clasă sunt scoase din opționale, obțin ceea ce vreau, ca argumentele să fie opționale și să le pot folosi în metodele clasei. Mai jos codul soluției:

type BoxParams = {
  x?: number;
  y?: number;
  height?: number;
  width?: number;
}

/** @class */
class Box {
  public x: number;
  public y: number;
  public height: number;
  public width: number;

  // The Box class can work double-duty as the interface here since they are identical
  // If you choose to add methods or modify this class, you will need to
  // define and reference a new interface for the incoming parameters object 
  // e.g.:  `constructor(params: BoxObjI = {} as BoxObjI)` 
  constructor(params: BoxParams = {} as BoxParams) {
    // Define the properties of the incoming `params` object here. 
    // Setting a default value with the `= 0` syntax is optional for each parameter
    const {
      x = 0,
      y = 0,
      height = 1,
      width = 1,
    } = params;

    //  If needed, make the parameters publicly accessible
    //  on the class ex.: 'this.var = var'.
    /**  Use jsdoc comments here for inline ide auto-documentation */
    this.x = x;
    this.y = y;
    this.height = height;
    this.width = width;
  }

  method(): void {
    const total = this.x + 1;
  }
}

const box1 = new Box();
const box2 = new Box({});
const box3 = new Box({ x: 0 });
const box4 = new Box({ x: 0, height: 10 });
const box5 = new Box({ x: 0, y: 87, width: 4, height: 0 });

Apreciez comentariile celor care își fac timp să citească și să încerce să înțeleagă ce vreau să spun.

Mulțumesc anticipat.

Comentarii

  • Da, exact așa trebuie folosită metoda mea atunci când se fac personalizări (comentariul de deasupra constructorului direcționează către soluția exactă pe care o aveți aici). Câțiva oameni s-au împiedicat de asta – furtul meu de interfață din clasă – așa că sunt tentat să-mi modific răspunsul. Dar îl voi lăsa pentru istorie, deoarece faptul că răspunsul meu „așa cum este” este un punct de referință necesar în răspunsul tău minunat de aici. –  > Por Benson.
  • OK, înțeleg. Mulțumesc pentru clarificări –  > Por rustyBucketBay.
Adriano

Ar trebui să aveți în vedere că…

contructor()

constructor(a:any, b:any, c:any)

Este la fel ca și new() sau new("a","b","c")

Astfel

constructor(a?:any, b?:any, c?:any)

este la fel ca mai sus și este mai flexibil…

new() sau new("a") sau new("a","b") sau new("a","b","c")