typescript - Bekreft at valgfrie egenskaper i grensesnitt ikke er null i klasse ved kompileringstid


I Javascript liker jeg å sende et options-objekt til en konstruktør, og fylle inn eventuelle manglende egenskaper jeg forventet med standardverdier:

function foo(options) {
   if(options.a == undefined) {
      options.a = 1;
   }
   if(options.b == undefined) {
      options.b = 'hello';
   }
   this.options = options;
}

new foo({a: 10});

Prøver å implementere dette mønsteret i Typescript, jeg har:

interface Options {
    a?: number;
    b?: string;
}

class Foo {
   options: Options;
   constructor(options: Options) {
      if(options.a == undefined) {
         options.a = 1;
      }
      if(options.b == undefined) {
         options.b = 'hello';
      }
      this.options = options;
   }
}

Når jeg bruker options i class senere, vet jeg at alle egenskapene er definert. Imidlertid er ikke Typescript klar over dette, så denne uttalelsen gir en feil:

let a:Number = this.options.a; // Argument of type 'number | undefined' is not assignable to parameter of type number.

I tillegg vil jeg ikke tillate at kode i class senere tildeler en null eller undefined verdi til egenskapen:

this.options.a = undefined; // should produce compiler error

Finnes det en måte å definere typene mine på, slik at egenskapene til Options ikke er nullable når de brukes i class uten å definere to Options-grensesnitt?

For eksempel vet jeg at jeg kunne lage et annet interface for internt bruk:

interface OptionsSafe {
    a: number;
    b: string;
}

// in Foo constructor
this.options = {
    a: a != undefined ? options.a! : 1,
    b: b != undefined ? options.b! : 'hello'
};

Men da må jeg definere egenskapene til alternativene i to separate grensesnitt og sørge for at de alltid er synkronisert (denne løsningen ville vært flott hvis Typescript kan generere begge grensesnittene fra én definisjon).

Jeg vil heller ikke sende alle egenskapene som individuelle, valgfrie argumenter, fordi

  1. det kan være dusinvis eller flere alternativer og
  2. funksjonsargumentene kunne avvike fra Options-grensesnittet, så jeg må fortsatt holde to "grensesnitt" synkronisert.

Svar #1

Et alternativ er å bruke den innebygde Partial-typen som Typescript tilbyr. Dette kan være for nært «å definere to Options-grensesnitt» for deg, men jeg skal vise deg hva jeg mener uansett.

Typescript tilbyr en innebygd type Partial som er definert som type Partial<T>={[P in keyof T]?: T[P]}.

Hva dette betyr er at du kan bare definere Options-grensesnittet med alle medlemmene, men bruke den delvise versjonen av det for konstruktøren din. Slik:

interface Options {
    a: number;
    b: string;
}

class Foo {
   options: Options;
   constructor(options: Partial<Options>) {
      const defaultOptions = {
          a: 1,
          b: 'hello'
      };

      this.options = Object.assign(defaultOptions, options);
   }
}

For at Typescript skal kunne korrekt slutte at du har alle nødvendige egenskaper på objektet, må du gå bort fra if-setningslogikken du brukte, til noe mer som det ovenfor.

Relaterte artikler