void vs never
void oznacza, że funkcja nie zwraca użytecznej wartości, a never, że funkcja nigdy nie wraca: rzuca błąd albo działa w nieskończoność.
Porównanie w tabeli
#Funkcja kończy się normalnie
Rzeczywisty wynik w runtime
Można przypisać wartość tego typu do zmiennej
Sprawdzanie kompletności unii
Typ funkcji zwrotnej (callback)
Typowe użycie
Przykład
#function logProgress(done: number, total: number): void {
console.log(`Postęp: ${Math.round((done / total) * 100)}%`)
}
function fail(message: string): never {
throw new Error(message)
}
const result = logProgress(3, 4)
console.log(result)
try {
fail('coś poszło nie tak')
} catch (error) {
console.log((error as Error).message)
}Postęp: 75% undefined coś poszło nie tak
Definicja i zastosowanie
#Oba typy opisują najczęściej wartość zwracaną przez funkcję i używa się ich tam, gdzie zwykła wartość nie ma sensu. Żaden z nich nie pozwala na sensowne dalsze operacje na wyniku.
Różnica jest zasadnicza. Funkcja z typem void normalnie się kończy, po prostu nie zwraca niczego użytecznego, a w praktyce jej wynikiem jest undefined. Funkcja z typem never nigdy nie wraca do miejsca wywołania: albo rzuca wyjątek, albo działa w nieskończonej pętli. never pojawia się też jako typ wartości, które nie mogą wystąpić, np. w gałęzi default po obsłużeniu wszystkich wariantów unii, czego void nigdy nie wyraża.
Typ void dopisuj do funkcji, które wykonują efekt, np. logują, zapisują albo zmieniają stan, i których wyniku nikt nie powinien używać. Typ never zostaw dla funkcji pomocniczych, które zawsze rzucają błąd, i dla sprawdzania kompletności unii w switch. Nigdy nie używaj never jako typu zmiennej, do której chcesz coś przypisać, bo żadna wartość do niego nie pasuje.
Składnia
#function nazwa(): void { … }
function fail(): never { throw new Error() }Więcej przykładów
#type Command = { kind: 'start' } | { kind: 'stop' }
function assertNever(value: never): never {
throw new Error(`Nieznana komenda: ${JSON.stringify(value)}`)
}
function run(command: Command): string {
switch (command.kind) {
case 'start':
return 'Start'
case 'stop':
return 'Stop'
default:
return assertNever(command)
}
}
console.log(run({ kind: 'start' }))
console.log(run({ kind: 'stop' }))Start Stop
const log: (message: string) => void = (message) => {
console.log(message.toUpperCase())
return message.length
}
log('html')HTML
Mimo że przekazana funkcja realnie coś zwraca, typ (message: string) => void każe to zignorować. Dzięki temu funkcje jak arr.forEach(x => list.push(x)) są poprawne, choć push() zwraca liczbę.
Dobre praktyki
#- Dopisuj
voidjawnie do funkcji, które wykonują efekt uboczny. To mówi czytelnikowi, że nie powinien używać wyniku. - Funkcja
assertNeverw gałęzidefaultto prosty sposób, żeby kompilator przypominał o nowych wariantach unii. - Nie próbuj zadeklarować zmiennej typu
never, do której chcesz coś przypisać. Żadna wartość, nawetundefined, nie pasuje do tego typu.
Powiązane hasła
#- voidTyp zwracany przez funkcje, które nic nie zwracają.
- neverTyp wartości, która nigdy nie wystąpi: funkcji, które nie kończą się normalnie, i niemożliwych przypadków.
- Typy funkcjiTypowanie parametrów, wartości zwracanej i samych funkcji jako wartości.
- Unie rozróżnialneUnie obiektów ze wspólnym polem-etykietą, które pozwala łatwo odróżnić warianty.
Widzisz błąd albo brakuje przykładu? Napisz do nas.