Home > other >  Go: How to use enum as a type?
Go: How to use enum as a type?

Time:04-20

I have a list of events (enum) which defines the particular event:

package events

const (
    NEW_USER       = "NEW_USER"
    DIRECT_MESSAGE = "DIRECT_MESSAGE"
    DISCONNECT     = "DISCONNECT"
)

And there is a struct that will use this enum as one of its attribute

type ConnectionPayload struct {
    EventName    string      `json:"eventName"`
    EventPayload interface{} `json:"eventPayload"`
}

Is there a way I can use enum as a type for EventName instead of string?

This is possible in typescript not sure how to do it in golang.

I want the developers to forcefully use correct event name using the enum instead of making a mistake by using any random string for eventname.

CodePudding user response:

You can do it by generating code like the below.


type EventNames string

const (
    NEW_USER       EventNames = "NEW_USER"
    DIRECT_MESSAGE EventNames = "DIRECT_MESSAGE"
    DISCONNECT     EventNames = "DISCONNECT"
)

then change your struct to this:

type ConnectionPayload struct {
    EventName    EventNames  `json:"eventName"`
    EventPayload interface{} `json:"eventPayload"`
}

CodePudding user response:

Try this:

package main

import (
    "fmt"

    "gopkg.in/go-playground/validator.v9"
)

type EventName string

const (
    NEW_USER       EventName = "NEW_USER"
    DIRECT_MESSAGE EventName = "DIRECT_MESSAGE"
    DISCONNECT     EventName = "DISCONNECT"
)

type ConnectionPayload struct {
    EventName    EventName   `json:"eventName" validate:"oneof=NEW_USER DIRECT_MESSAGE DISCONNECT"`
    EventPayload interface{} `json:"eventPayload"`
}

func (s *ConnectionPayload) Validate() error {
    validate := validator.New()
    return validate.Struct(s)
}

func main() {
    a := ConnectionPayload{
        EventName: "NEW_USER",
    }
    b := ConnectionPayload{
        EventName: "NEW_USERR",
    }
    err := a.Validate()
    fmt.Println(a, err)
    err = b.Validate()
    fmt.Println(b, err)
}

CodePudding user response:

There is no enum type at the moment in go, and there currently isn't a direct way to enforce the same rules as what typescript does.


A common practice in go is to use the suggestion posted by @ttrasn :

define a custom type, and typed constants with your "enum" values :

type EventName string

const (
    NEW_USER       EventName = "NEW_USER"
    DIRECT_MESSAGE EventName = "DIRECT_MESSAGE"
    DISCONNECT     EventName = "DISCONNECT"
)

This allows you to flag, in your go code, the places where you expect such a value :

// example function signature :
func OnEvent(e EventName, id int) error { ... }

// example struct :
type ConnectionPayload struct {
    EventName    EventName  `json:"eventName"`
    EventPayload interface{} `json:"eventPayload"`
}

and it will prevent assigning a plain string to an EventName

var str string = "foo"
var ev EventName

ev = str // won't compile
OnEvent(str, 42) // won't compile

The known limitations are :

  • in go, there is always a zero value :
    var ev EventName  // ev is ""
    
  • string litterals are not the same as typed variables, and can be assigned :
    var ev EventName = "SOMETHING_ELSE"
    
  • casting is allowed :
    var str string = "foo"
    var ev EventName = EventName(str)
    
  • there is no check on unmarshalling :
    jsn := []byte(`{"eventName":"SOMETHING_ELSE","eventPayload":"some message"}`)
    err := json.Unmarshal(jsn, &payload) // no error
    

https://go.dev/play/p/vMUTpvH8DBb

If you want some stricter checking, you would have to write a validator or a custom unmarshaler yourself.

  • Related