Swift で API/DB から取得した値の ID 型指定はたぶん通常 String
や Int
などを指定するのが一般的なのかな?と思う。
メソッド等に ID を渡すときに、プリミティブ型だと渡したい ID を取り違えたときにスッとコンパイル通ってしまって困る。
Scala には値クラスっていうのが存在して、ある型に別名を付けて、それと区別するみたいなことが出来る(理解が曖昧かつ雑)。
値クラスと汎用トレイト | Scala Documentation
要は、以下のようなことがしたい。
struct UserID: AnyVal { let value: String } struct MediaID: AnyVal { let value: String } var userID = "1234" let mediaID = "4321" // ↓ここでコンパイルエラーが出て欲しい userID = mediaID
なんかそういうことするの難しそうで、とりあえずメソッド等の定義に使えればいいやということで、typealias
でお茶を濁していた。
だけどメソッドの引数に指定した UserID
にミスって MediaID
を渡してしまってもコンパイル通ってしまう。
テストも人間が書くものなので100%信用できるわけではない。
ということで、いろいろ調べてそれっぽいことが出来そうなコードを書いてみた。 さっきそれっぽいところまで行ったので、まだ開発中のアプリで使ったりはしていない。
struct UserID: ExpressibleByStringLiteral, CustomStringConvertible, CustomDebugStringConvertible, RawRepresentable, Hashable, Equatable, Codable { private(set) var rawValue: String var description: String { return rawValue } var debugDescription: String { return "\(String(describing: type(of: self)))(\(rawValue))" } init(stringLiteral value: String) { self.rawValue = value } init?(rawValue: String) { self.rawValue = rawValue } } struct MediaID: ExpressibleByStringLiteral, CustomStringConvertible, CustomDebugStringConvertible, RawRepresentable, Hashable, Equatable, Codable { private(set) var rawValue: String var description: String { return rawValue } var debugDescription: String { return "\(String(describing: type(of: self)))(\(rawValue))" } init(stringLiteral value: String) { self.rawValue = value } init?(rawValue: String) { self.rawValue = rawValue } } var userID: UserID = "1" print(userID) // => 1 userID = "2" print(userID) // => 2 print(userID.debugDescription) // => UserID(2) // ↓Error🎉 // let mediaID: MediaID = "2" // userID = mediaID print(userID == "0") // => false print(userID == "1") // => false print(userID == "2") // => true let string: String = userID.rawValue print(string) // => 2 // Equatable 追加すれば以下も可能 // print(userID == UserID(rawValue: "2")) // => true
一応出来たぞ〜〜〜って感じでこれ以上書くことが無いんだけど、いい方法知ってたりしたら教えてください!!!!!!!!!!!!!!!!!!!!!!