This is just a case of non-idiomatic programming in the first place, and it's not even non-idiomatic "Go" programming, it's non-idiomatic for object-oriented languages in general. The correct solution to this sort of problem is more like this:

type OptionParser interface {
ParseValue(data string) (*Value, error)
}

type OptionField struct {}

func (of *OptionField) ParseValue(data string) (*Value, error) {
return &Value{OptionID: data}, nil
}

type NumericField struct {}

func (nf *NumericField) ParseValue(data string) (*Value, error) {
value, err := strconv.ParseFloat(data, 64)
if err != nil {
return nil, errors.New("invalid value, must be a number")
}

return &Value{Numeric: value}, nil
}

and so on. A non-Go language would use whatever the local Abstract Data Type abstraction is. The parseValue function just melts away entirely, you just call a method on the value you have in hand. And now, instead of having already spent a great deal of design budget on this abstraction, and having a hard time integrating it into any future abstractions you may want to make, you're using language constructs in the way they were designed, and there's more space here to make further adaptations. Generics could even help with some of those perhaps, but they're not needed here.

It's possible the Value type could be helped a bit with generics, but it's not clear. Probably the old-school existing "sum types" techniques would work just fine: http://www.jerf.org/iri/post/2917 But "a field per possible value" isn't necessarily a bad choice, especially if it's only a handful of fields. Sometimes it's the best performing implementation, even if it may seem skeevy at the programmer level, because it involves the least indirection even if it seems to involve the largest (in RAM size) objects, and being able to pass them around by value rather than pointers or interfaces means inlining may end up doing more for you too. In general, nowadays, you're better off using 64 bytes in one place than you are using 16 bytes here and 16 bytes somewhere else through a pointer, even if 64 > 32.

It is helpful to understand the expression problem to understand what's going on here: https://en.wikipedia.org/wiki/Expression_problem In short, OO excels in adding values that implement an existing set of operations, functional programming excels in adding operations to existing sets of data types. Forcing sum types here into a language that doesn't really want them means that you're taking a techique that is good at the wrong dimension (you are adding more data types here that fit into the existing operations, what OO is already good at), then having to twist it around in a way it isn't really comfortable with to get it to work with what you want (in this case it'll manifest as needing to add this code to every method, of which I'm sure you have more than the ones shown; in properly-applied FP you get usually get the advantage by adding many fewer clauses to your pattern matches, often just one), when the correct solution was already there.

While I often complain about trying to jam FP into OO without understanding in the context of map/filter/reduce-based programming, this is actually the even more profound difference between the two. It's nice to have the FP toolkit around in an OO language for when you encounter a problem it can help with, but when you're in an OO language and right in OO's wheelhouse, use the OO. Don't jam the FP in because FP is Platonically better or something. It actually has well-characterizable weaknesses vs. object orientation, and at least as it stands now is absolutely not universally better at everything.

(Some up-and-coming languages claim to have solved the expression problem. I have not evaluated any of these claims. More power to them if they are correct!)