2016-09-09 13 views
23

Mam kod:Jak sprawdzić, czy blok przełącznika jest wyczerpujący w języku TypeScript?

enum Color { 
    Red, 
    Green, 
    Blue 
} 

function getColorName(c: Color): string { 
    switch(c) { 
     case Color.Red: 
      return 'red'; 
     case Color.Green: 
      return 'green'; 
     // Forgot about Blue 
    } 

    throw new Error('Did not expect to be here'); 
} 

zapomniałem sobie sprawę Color.Blue i wolałbym zdobyć błąd kompilacji. Jak mogę ustrukturyzować mój kod tak, aby TypeScript oznaczało to jako błąd?

Odpowiedz

31

W tym celu użyjemy typu never (wprowadzonego w TypeScript 2.0), który reprezentuje wartości, które "nie powinny" występować.

Pierwszym krokiem jest napisanie funkcji:

function assertUnreachable(x: never): never { 
    throw new Error("Didn't expect to get here"); 
} 

następnie użyć go w przypadku default (lub równoważnie, poza przełącznikiem):

function getColorName(c: Color): string { 
    switch(c) { 
     case Color.Red: 
      return 'red'; 
     case Color.Green: 
      return 'green'; 
    } 
    return assertUnreachable(c); 
} 

W tym momencie, zobaczysz błąd:

return assertUnreachable(c); 
     ~~~~~~~~~~~~~~~~~~~~~ 
     Type "Color.Blue" is not assignable to type "never" 

komunikat o błędzie wskazuje przypadki zapomniane umieścić w swoim wyczerpujący przełącz! Jeśli przerwiesz wiele wartości, zobaczysz błąd dotyczący np. Color.Blue | Color.Yellow.

Pamiętaj, że jeśli używasz strictNullChecks, będziesz potrzebować tego return przed wywołaniem assertUnreachable (w przeciwnym razie jest to opcjonalne).

Możesz dostać trochę hodowcy, jeśli chcesz. Jeśli na przykład korzystasz z dyskryminowanej unii, może być przydatne odzyskanie właściwości dyskryminacyjnej w funkcji asercji w celu debugowania. Wygląda to tak:

// Discriminated union using string literals 
interface Dog { 
    species: "canine"; 
    woof: string; 
} 
interface Cat { 
    species: "feline"; 
    meow: string; 
} 
interface Fish { 
    species: "pisces"; 
    meow: string; 
} 
type Pet = Dog | Cat | Fish; 

// Externally-visible signature 
function throwBadPet(p: never): never; 
// Implementation signature 
function throwBadPet(p: Pet) { 
    throw new Error('Unknown pet kind: ' + p.species); 
} 

function meetPet(p: Pet) { 
    switch(p.species) { 
     case "canine": 
      console.log("Who's a good boy? " + p.woof); 
      break; 
     case "feline": 
      console.log("Pretty kitty: " + p.meow); 
      break; 
     default: 
      // Argument of type 'Fish' not assignable to 'never' 
      throwBadPet(p); 
    } 
} 

To jest ładny wzór, ponieważ zapewniasz bezpieczeństwo podczas kompilacji, aby upewnić się, że załatwiłeś wszystkie sprawy, których się spodziewałeś. A jeśli dostaniesz naprawdę zewnętrzną właściwość (np. Jakiś wywołujący JS wymyślił nowy species), możesz rzucić przydatny komunikat o błędzie.

+0

Przy włączonej opcji "strictNullChecks', nie wystarczy zdefiniować typ zwracany przez funkcję jako' string', bez konieczności używania funkcji 'assertUnreachable'? – dbandstra

+1

@dbandstra Z pewnością możesz to zrobić, ale jako ogólny wzór assertUnreachable jest bardziej niezawodny. Działa nawet bez 'strictNullChecks', a także kontynuuje pracę, jeśli istnieją warunki, w których chcesz wrócić niezdefiniowany z zewnątrz przełącznika. – Letharion

Powiązane problemy