Skip to content

Uninitialized properties without exclamation mark should cause to compile error #482

Closed

Description

@MaxGraey
class Foo {
  notAssigned: f64; // <-- should be compile error "Property 'notAssigned' has no initializer and is not definitely assigned in the constructor."
  nonAssignedButIgnored!: f64; // has exclamation mark so valid and init as zero (default)
}
class Foo {
  notAssigned: f64; // ok! Now assigned in constructor
  nonAssignedButIgnored!: f64; // ok!
  constructor() {
     this.notAssigned = 0;
  }
}

Activity

  1. saulecabrera commented on Feb 25, 2020

    @saulecabrera
    Contributor

    Hey, @MaxGraey I'd like to take a stab at this one, but first I have some assumptions that I'd like to sort out.

    In general, I agree that in the base case when a property is not initialized, compilation should fail, just like TS does through --strictPropertyInitialization

    Now, for properties that make use of definite assignment assertions(!) do we want to initialize them with a default value as you are suggesting?

    nonAssignedButIgnored!: f64; // has exclamation mark so valid and init as zero (default)

    The only reason I'm asking this is that I'm thinking what would be the expectation in this case with non-basic types?

    • For basic types, I think it makes sense since they can't be null
    • For complex types, we could initialize them as well but it feels counterintuitive in terms of TypeScript compatibility.
    • Another option for complex types, we could do what Typescript does: it doesn't throw an error if the property is prefixed with ! and assumes the property will be initialized later but prior to its usage.

    Thoughts?

  2. MaxGraey commented on Feb 25, 2020

    @MaxGraey
    MemberAuthor

    definite assignment assertions is pretty similar to post-fix expression operator (non-null assertions) after nullable properties. It force to unsound behaviour which potentially dangerous so required runtime checks. So when we have assignment assertions for non-nullable references like that:

    class Foo {
      nonAssignedButIgnored!: SomeRef
    }

    I guess we should do extra runtime check every time when try to access to this property like:

    let foo = new Foo;
    let res = foo.nonAssignedButIgnored.someMethod();

    will be actually:

    let res = foo.nonAssignedButIgnored !== null 
      ? foo.nonAssignedButIgnored.someMethod() 
      : unreachable();

    Same approach already used for non-null assertions

    @dcodeIO wdyt?

  3. dcodeIO commented on Feb 25, 2020

    @dcodeIO
    Member

    Definitive assignment fields feel a bit strange in a strict type context to me. My expectation so far was that we'd have field flags, like we have local flags for "possibly null", in flows eventually that would track whether a non-nullable reference typed field has been initialized either with an explicit initializer or after the constructor ran, so definitive assignment would be implicit in such scenarios. Everything else than asserting after the constructor would most likely require a runtime check as you outlined above.

  4. MaxGraey commented on Feb 25, 2020

    @MaxGraey
    MemberAuthor

    Yeah, it will be great have "undefined tracking" same as nullable tracking what we already have with fallback to runtime check when impossible do this in compile time.

  5. saulecabrera commented on Feb 28, 2020

    @saulecabrera
    Contributor

    Thanks for the clarifications, all of that makes sense.

    While working on this, I also found about:

    class Bar {
       public run(): i32 {
          return 42;
       }
    }
    
    class Foo {
       public bar: Bar | null;
    
       constructor() {
          this.bar = null;
       }
    }
    
    export function run(): i32 {
       const foo = new Foo();
       return foo.bar.run();
    }

    When executing run I get 42 but I was expecting unreachable; I might be lacking some context, is this because we're implicitly initializing Bar?

  6. MaxGraey commented on Feb 28, 2020

    @MaxGraey
    MemberAuthor

    When executing run I get 42 but I was expecting unreachable

    Hmm this interesting. If you write with exclamation:

    const foo = new Foo();
    return foo.bar!.run();

    you got unreachable which actually expected.
    cc @dcodeIO

  7. dcodeIO commented on May 27, 2020

    @dcodeIO
    Member

    Closing this issue as part of 2020 vacuum because it is meanwhile tracked by a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions