2017-07-31 3 views
0

Mon code TS est compilé en utilisant strictNullChecks et noImplicitAny, et j'ai beaucoup d'options à traiter. Je souhaite utiliser une fonction de vérificateur booléen pour indiquer si la variable (et d'autres variables connexes) doit être définie ou non. Exemple simplifié:Affirmer plusieurs variables connexes comme non nul et non indéfini basé sur un indicateur booléen

class Person { 
    private firstName: string; 
    private firstNameSyllables: number; 

    private middleName?: string; 
    private middleNameSyllables?: number; 
    private hasMiddleName: boolean; 

    constructor(firstName: string, middleName?: string){ 
     this.firstName = firstName; 
     this.firstNameSyllables = calculateSyllables(firstName); 

     if(typeof middleName !== "undefined"){ 
      this.middleName = middleName; 
      this.middleNameSyllables = calculateSyllables(middleName); 
      this.hasMiddleName = true; 
     } else { 
      this.hasMiddleName = false; 
     } 
    } 

    // Stub function 
    calculateSyllables(name: string): number { return name.length/3; } 
    hasMiddleNameCheck(): boolean { return this.hasMiddleName; } 

    getStartOfNameWithSyllables(): string { 
     if(this.hasMiddleNameCheck()){ 
      // ERROR: middleName and middleNameSyllables may be undefined 
      return `${this.firstName}: ${this.firstNameSyllables},` + 
        `${this.middleName}: ${this.middleNameSyllables}`; 
     } else { 
      return `${this.firstName}: ${this.firstNameSyllables}`; 
     } 
    } 
} 

Comment puis-je obtenir le compilateur de déduire que les deux middleName et middleNameSyllables doivent être définis, à la suite de this.hasMiddleNameCheck() retour true? Actuellement je dois contourner le problème en utilisant des assertions de type telles que middleName as string ou middleName!, mais cela n'est pas souhaitable lors de refacteurs qui rendent les variables facultatives, car cela implique de suivre chaque utilisation d'une variable éventuellement indéfinie.

+0

Qu'en est-il de la valeur 'null'? Pourquoi voulez-vous utiliser un indicateur booléen si vous pouvez vérifier le champ lui-même pour avoir une valeur ou non?Il y a des limites au compilateur - ce n'est pas magique. –

+0

Pourquoi ne pas simplement abandonner le drapeau et vérifier explicitement 'if (this.middleName! = Null && thisMiddleNameSyllables! = Null)' qui correspond alors clairement au contenu des énoncés suivants? Le 'x! = Null' est vrai pour' x === null' et 'x === undefined'. – Duncan

Répondre

1

vous pouvez faire cela avec un special type de user-defined type guard si vous permettent de changer certains de vos private membres à protected:

class Person { 
    ... 
    protected middleName?: string; 
    protected middleNameSyllables?: number; 
    protected hasMiddleName: boolean; 
    ... 
} 

et après la déclaration Person vous pouvez déclarer une nouvelle sous-classe appelée PersonWithMiddleName (que vous n'êtes jamais allez instancier en fait):

class PersonWithMiddleName extends Person { 
    hasMiddleName: true; 
    middleName: string; 
    middleNameSyllables: number; 
} 

Enfin, changer le type de retour de hasMiddleNameCheck():

hasMiddleNameCheck(): this is PersonWithMiddleName { 
    return this.hasMiddleName; 
} 

Et tout devrait fonctionner pour vous. La nature des membres que vous essayez de garder rend la chose plus compliquée; si elles étaient publiques, vous n'auriez pas besoin de sous-classer Person.


Si cela se sent trop alambiquée pour vous, vous pouvez effectuer les variables liées d'un seul objet comme

interface MiddleName { 
    name: string; 
    syllables: number; 
} 

et alors Person ont une option MiddleName:

class Person { 
    ... 
    private middleName?: MiddleName; 
    ... 
} 

Ensuite, il vous suffit de vérifier si this.middleName est défini, et accéder this.middleName.name et this.middleName.syllables, qui n'est pas muc h plus verbeux et rend très évident que ceux-ci sont tous définis ou tous indéfinis.

Espérons que cela aide. Bonne chance!

+0

Le premier est une belle solution; juste ce que je cherchais, merci! La seconde solution fonctionne pour les cas simples, mais pas quand une propriété optionnelle peut être trouvée définie dans deux situations différentes (par exemple, on ne pourrait pas simplement imbriquer la propriété optionnelle sous une classe comme «MiddleName»). –

0

La solution la plus simple serait de ne pas avoir this.middleNameSyllables en option, mais de le remplacer par défaut par 0. Combinez cela avec un contrôle explicite pour this.middleName != null au lieu de la variable de drapeau et le compilateur sera heureux avec les deux valeurs:

private middleNameSyllables: number = 0; 
... 
getStartOfNameWithSyllables(): string { 
    if(this.middleName != null){ 
     return `${this.firstName}: ${this.firstNameSyllables},` + 
       `${this.middleName}: ${this.middleNameSyllables}`; 
    } else { 
     return `${this.firstName}: ${this.firstNameSyllables}`; 
    } 
} 

Ou si vous préférez garder le numéro en option, ainsi, il suffit de cocher les deux valeurs dans l'état et le nouveau type inférences ne saura que ni est undefined lorsque vous l'utilisez:

getStartOfNameWithSyllables(): string { 
    if(this.middleName != null && this.middleNameSyllables != null){ 
     return `${this.firstName}: ${this.firstNameSyllables},` + 
       `${this.middleName}: ${this.middleNameSyllables}`; 
    } else { 
     return `${this.firstName}: ${this.firstNameSyllables}`; 
    } 
}