typescript - Cum să apelezi returnul dintr-un constructor fără a marca proprietățile ca fiind undefined?
Folosesc un fișier de constante exportate ca directive de preprocesare. Acum aș dori să elimin complet corpul unui constructor dacă această directivă este setată. De exemplu:
const MY_PREPROCESSOR_DIRECTIVE = true;
class Foo {
someProp: string;
constructor() {
if (MY_PREPROCESSOR_DIRECTIVE) return;
this.someProp = "string";
}
}
Funcționează bine, folosesc compilatorul Closure pentru construcție, care elimină corpul și totul este transformat în doar:
class Foo {}
În TypeScript acest lucru cauzează erori, totuși, deoarece someProp și otherProp nu sunt „neapărat atribuite în constructor”. Iar în fișierele JavaScript, nu primesc erori în constructor, dar toate proprietățile sunt setate la string | undefined:
const MY_PREPROCESSOR_DIRECTIVE = false;
class Foo {
constructor() {
if (MY_PREPROCESSOR_DIRECTIVE) return;
this.someProp = "string";
}
myMethod() {
if (MY_PREPROCESSOR_DIRECTIVE) return;
this.someProp.substring(0);
// ^^^^^^^^^^^^^------ Object is possibly 'undefined'.
}
}
(teren de joacă js | teren de joacă ts)
Există o modalitate de a face TypeScript să știe că directiva va fi întotdeauna falsă, astfel încât să nu marcheze this.someProp ca string | undefined? Am încercat să marchez instrucțiunea return din constructor ca cod inaccesibil, dar nu sunt sigur cum să realizez acest lucru.
Răspuns #1
Deoarece TS nu știe nimic bout runtime MY_PREPROCESSOR_DIRECTIVE` cred că merită să folosim pattern-ul de strategie.
Așadar, aveți două variante ale clasei Foo. Una cu MY_PREPROCESSOR_DIRECTIVE=true și alta cu MY_PREPROCESSOR_DIRECTIVE=false.
const MY_PREPROCESSOR_DIRECTIVE = false;
interface Foo {
someProp?: string;
myMethod: () => void
}
class WithDirective implements Foo {
someProp: string;
constructor() {
this.someProp = "string";
}
myMethod() {
this.someProp.substring(0)
}
}
class WithoutDirective implements Foo {
someProp: undefined
constructor() { }
myMethod() { }
}
const Foo = MY_PREPROCESSOR_DIRECTIVE ? WithDirective : WithoutDirective
Teren de joacă
Dacă nu-ți place faptul că va exista întotdeauna o clasă neutilizată într-o construcție, poți folosi o expresie de clasă:
const MY_PREPROCESSOR_DIRECTIVE = false;
interface Foo {
someProp?: string;
myMethod: () => void
}
const Foo =
MY_PREPROCESSOR_DIRECTIVE
? class implements Foo {
someProp: string;
constructor() {
this.someProp = "string";
}
myMethod() {
this.someProp.substring(0)
}
}
: class implements Foo {
someProp: undefined
constructor() { }
myMethod() { }
}
Personal, cred că primul exemplu este mai ușor de citit și mai ușor de întreținut. De asemenea, nu cred că va avea un impact semnificativ asupra dimensiunii pachetului tău.
Răspunsul #2
Se pare că o modalitate de a evita această problemă, oricum în JavaScript, este să setezi explicit tipul folosind JSDoc:
constructor() {
if (MY_PREPROCESSOR_DIRECTIVE) return;
/** @type {string} */
this.someProp = "string";
}
(loc de joacă)
Nu sunt sigur dacă există o metodă similară de a face asta cu TypeScript, așa că acest comportament ar putea foarte bine să fie neintentionat.
