使用新型別改善型別安全性
別名型別減少了樣板並提高了可讀性,但它不比別名型別本身更安全。考慮以下:
type alias Email = String
type alias Name = String
someEmail = "holmes@private.com"
someName = "Benedict"
sendEmail : Email -> Cmd msg
sendEmail email = ...
使用上面的程式碼,我們可以編寫 sendEmail someName
,它會編譯,即使它真的不應該,因為儘管名稱和電子郵件都是 String
s,但它們完全不同。
通過建立一個新**型別,**我們可以在型別級別上真正區分一個 String
和另一個 String
。這是一個將 Email
重寫為 type
而不是 type alias
的示例:
module Email exposing (Email, create, send)
type Email = EmailAddress String
isValid : String -> Bool
isValid email =
-- ...validation logic
create : String -> Maybe Email
create email =
if isValid email then
Just (EmailAddress email)
else
Nothing
send : Email -> Cmd msg
send (EmailAddress email) = ...
我們的 isValid
函式可以確定字串是否是有效的電子郵件地址。create
函式檢查給定的 String
是否是有效的電子郵件,返回 Maybe
-wrapped Email
以確保我們只返回有效的地址。雖然我們可以通過編寫 EmailAddress "somestring"
直接構造 Email
來回避驗證檢查,如果我們的模組宣告沒有暴露 EmailAddress
建構函式,如此處所示
module Email exposing (Email, create, send)
那麼沒有其他模組可以訪問 EmailAddress
建構函式,儘管他們仍然可以在註釋中使用 Email
型別。在此模組之外構建新 Email
的唯一方法是使用它提供的 create
函式,該函式確保它首先只返回有效的電子郵件地址。因此,此 API 通過其型別安全性自動引導使用者沿著正確的路徑:send
僅適用於由執行驗證的 create
構造的值,並且因為它返回 Maybe Email
而強制處理無效的電子郵件。
如果你想匯出 Email
建構函式,你可以寫
module Email exposing (Email(EmailAddress), create, send)
現在任何匯入 Email
的檔案也可以匯入其建構函式。在這種情況下,這樣做將允許使用者迴避驗證和無效的電子郵件,但你並不總是構建這樣的 API,因此匯出建構函式可能很有用。對於具有多個建構函式的型別,你可能還只想匯出其中的一些建構函式。