Typowanie nominalne w TypeScript
hejto.plZacznijmy od tego, że system typowania w TypeScript jest strukturalny, tzn. mając dwa interfejsy o takich samych polach to według kompilatora będą one kompatybilne. W skrócie: jeżeli coś wygląda jak kaczka to musi być kaczką! Stoi to w przeciwieństwie do języków obiektowych, w których dwa interfejsy muszą być "rodziną", aby były uznawane za kompilator za tożsame.
Zazwyczaj nie stanowi to problemu. Rozważmy przypadek, w którym w naszym systemie krążą wartości pieniężne i czasowe. Używamy aliasów: `type Money = number` i `type Time = number`. Nic nie stoi na przeszkodzie wziąć "pisiont" złotych i podać jako argument funkcji, która z założenia ma operować tylko na czasie. Według typowania strukturalnego wszystko się zgadza. Jak zabezpieczyć się przed takim błędem?
Na pomoc przychodzi wspomniane typowanie nominalne i zabieg nazywany brandingiem. Zdefiniujmy typ Money jako: `type Money = number & { __brand: 'Money' }`. Od tego momentu nie będziemy mogli przekazać typu `number` jako bytu tożsamego `Money`. Kompilator uchroni nas przed tym.
W układance brakuje jednego elementu - skąd brać wartość typu `Money`? Sprawa wygląda następująco: castowanie za pomocą `as` lub implementacja funkcji, która przy okazji sprawdzi, że wartość podawana konwersji może zostać przedstawiona jako `Money`. Tutaj akurat nie ma raczej czego sprawdzać, ale w przypadku czasu coś takiego mogłoby mieć miejsce, np. sprawdzalibyśmy czy wartość jest dodatnia.
#typescript #magicznytypescript
Zazwyczaj nie stanowi to problemu. Rozważmy przypadek, w którym w naszym systemie krążą wartości pieniężne i czasowe. Używamy aliasów: `type Money = number` i `type Time = number`. Nic nie stoi na przeszkodzie wziąć "pisiont" złotych i podać jako argument funkcji, która z założenia ma operować tylko na czasie. Według typowania strukturalnego wszystko się zgadza. Jak zabezpieczyć się przed takim błędem?
Na pomoc przychodzi wspomniane typowanie nominalne i zabieg nazywany brandingiem. Zdefiniujmy typ Money jako: `type Money = number & { __brand: 'Money' }`. Od tego momentu nie będziemy mogli przekazać typu `number` jako bytu tożsamego `Money`. Kompilator uchroni nas przed tym.
W układance brakuje jednego elementu - skąd brać wartość typu `Money`? Sprawa wygląda następująco: castowanie za pomocą `as` lub implementacja funkcji, która przy okazji sprawdzi, że wartość podawana konwersji może zostać przedstawiona jako `Money`. Tutaj akurat nie ma raczej czego sprawdzać, ale w przypadku czasu coś takiego mogłoby mieć miejsce, np. sprawdzalibyśmy czy wartość jest dodatnia.
#typescript #magicznytypescript