Skip to content

Design Meeting Notes, 10/20/2023 #56164

Closed
@DanielRosenwasser

Description

@DanielRosenwasser

isolatedModules and Cross-File Enum Member References

#56153

  • "Who doesn't love enums?"

  • If you have a numeric enum member in one file, and a numberic enum that references that first one in another file...

  • ...and a subsequent member has no initializer...

    // foo.ts
    enum Foo {
        A = 123,
    }
    
    // bar.ts
    import { Foo } from "./foo";
    
    enum Bar {
        B = Foo.A,
        C
    }
    import { Foo } from "./foo";
    export var Bar;
    (function (Bar) {
        Bar[Bar["B"] = 10] = "B";
        Bar[Bar["C"] = 11] = "C";
    })(Bar || (Bar = {}));
  • ...under isolatedModules we don't issue an error - we don't know what this is supposed to be.

  • We do when A is a string - and if it is, we generate a void 0 for the initializer - but not if it's numeric (as above).

    // foo.ts
    enum Foo {
        A = "hello",
    }
    
    // bar.ts
    import { Foo } from "./foo";
    
    enum Bar {
        B = Foo.A,
        C
    }
    import { Foo } from "./foo";
    export var Bar;
    (function (Bar) {
        Bar["B"] = "10";
        Bar[Bar["C"] = void 0] = "C";
    })(Bar || (Bar = {}));
  • Should we?

  • Probably?

  • A compiler doesn't know what it should perform - numbers get a reverse-map, strings don't.

    • Well, a compiler doesn't, but runtime emit can do a dynamic check.
      • Doing a lot of those is undesirable.
      • You only have to do one, right?
      • No, each uninitialized enum member may have to perform the same check.
        • (Is this true?)
  • Some philosophical discussion - should isolatedModules imply this dynamic behavior.

    • We won't emit these things, so how are compilers supposed to know what they should do?
    • On the other hand, there is nothing preventing compilers from making these enums "work".
  • We have a few choices.

    1. Force people to do the auto-numbering themselves.

      // bar.ts
      import { Foo } from "./foo";
      
      enum Bar {
          B = Foo.A,
          C = B + 1
      }
    2. Emit a dynamic check using typeof.

    3. Make a rule - enum members referencing another external enum must always be numeric, or explicitly converted to a string. Otherwise, it is an error. And under `isolatedModules``, we will have to assume nothing about the value of the enum member itself.

      • So we will assume B is a number, and use numeric reverse-mapping emit - but C will need an explicit initializer.
  • Conclusion: we are leaning towards the last-mentioned rule

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design NotesNotes from our design meetings

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions