Currently, creating an object variant type like this:
type
  FooKind = enum a, b, c
  Foo = object
    case kind: FooKind
    of a:
      bar, a: int
    of b:
      bar, b: int
    of c:
      c: int
results in an error:
Error: attempt to redefine: 'bar'
because the different branches of the variant cannot share fields. There's an open issue about that but since it's over 3 years old and still unresolved, I'm not holding my breath.
I see a few possible alternatives:
type
  FooKind = enum a, b, c
  Foo = object
    bar: int
    case kind: FooKind
    of a:
      a: int
    of b:
      b: int
    of c:
      c: int
type
  FooKind = enum a, b, c
  Foo[K: static[FooKind]] = object
    when K == a:
      bar, a: int
    elif K == b:
      bar, b: int
    elif K == c:
      c: int
type
  FooKind = enum a, b, c
  FooBase = object
    kind: FooKind
  FooA = object of FooBase
    bar, a: int
  FooB = object of FooBase
    bar, b: int
  FooC = object of FooBase
    c: int
Are there other alternatives to getting around this issue?
Making the "shared" fields common to all variants, which would pollute some variants with unnecessary fields
Is the way to go. And also "Using regular object inheritance" wouldn't accomplish the same at all.
Consider:
type
  Actions = enum
    Accuracy, Maturity, Id, Phase, Skip, Status
  Action = object
    case kind: Actions
    of Accuracy: acc: range[1..9]
    of Maturity: maturity: range[1..9]
    of Id: id: int
    of Phase:
      phasenote: string
      phase: range[1..5]
    of Skip:
      skipnote: string
      skip: int
    of Status:
      statusnote: string
      status: int
And say I don't like those foo-note fields. They're just notes; it's not important that they're notes for a status vs. some other kind of action.
Alternative #1 is to add a note: string field to the Action object, already stated.
Alternative #2:
type
  Actions = enum
    Accuracy, Maturity, Id, Phase, Skip, Status
  Action = object
    case kind: Actions
    of Accuracy: acc: range[1..9]
    of Maturity: maturity: range[1..9]
    of Phase: phase: range[1..5]
    of Id, Skip, Status:
      note: string
      id, skip, status: int
This moves the bloat to only three variants, instead of to all of them. Whether this is desirable or not vs. alternative #1 depends on your use.
I would like to discuss this one as well. I have hit a couple of cases including in Nim compiler itself when this features would have bern rather helpful. So it is potentially good addition to Nim as RFC.
Obviously, the following should not compile, because bar is out of position:
type
  FooKind = enum a, b, c
  Foo = object
    case kind: FooKind
    of a:
      bar, a: int
    of b:
      b, bar: int
    of c:
      c: int
While the following can:
type
  FooKind = enum a, b, c
  Foo = object
    case kind: FooKind
    of a:
      bar, a: int
    of b:
      bar, b: int
    of c:
      c: int
 Codegen should not be a problem. IsFieldAccessible check would become more involved though.
@Araq, what do you think? Is it worth an RFC?
Everything I said is still my opinion and I would write your example like:
type
  FooKind = enum a, b, c
  Foo = object
    case kind: FooKind
    of a, b: bar: int
    of a:
      a: int
    of b:
      b: int
    of c:
      c: int
Just hit this problem, solution proposed to @araq looks wrong. The point of type safety is to validate code, and having a field on the object that shouldn't be there is not contributing to correct code.
Some events have el, some don't. It makes no sense to force all events to have el attribute.
type EventType* = enum location, click, change
type Event* = object
  case kind*: EventType
  of location:
    location*: Url
  of click:
    el*:    seq[int]
    click*: ClickEvent
  of change:
    el*:     seq[int]
    change*: ChangeEvent
 It's better to fix language problems,, not mark it as "features" and forget.
PRs are welcome, talking is cheap. And no matter how you slice it, object variants in their current imperfect form are already quite useful. And you will never catch all your invariants within a traditional type system either, so every solution has these imperfections. It's just that you are not aware because the "invariant" keyword has not yet been added to TypeScript.