tunnelvisionlabs / antlr4ts

Optimized TypeScript target for ANTLR 4
Other
624 stars 106 forks source link

Property 'xxx' has no initializer and is not definitely assigned in the constructor. #477

Closed mike-lischke closed 3 years ago

mike-lischke commented 4 years ago

When "strictPropertyInitialization" is not specified or set to true then rules with a label will generate code that produces an error with Typescript. This rule:

routineOption:
    option = COMMENT_SYMBOL textLiteral
    | option = LANGUAGE_SYMBOL SQL_SYMBOL
    | option = NO_SYMBOL SQL_SYMBOL
    | option = CONTAINS_SYMBOL SQL_SYMBOL
    | option = READS_SYMBOL SQL_SYMBOL DATA_SYMBOL
    | option = MODIFIES_SYMBOL SQL_SYMBOL DATA_SYMBOL
    | option = SQL_SYMBOL SECURITY_SYMBOL security = (
        DEFINER_SYMBOL
        | INVOKER_SYMBOL
    )
;

contains 2 labels (option and security) and generates this code:

export class RoutineOptionContext extends ParserRuleContext {
    public _option: Token;
    public _security: Token;
    public textLiteral(): TextLiteralContext | undefined {
        return this.tryGetRuleContext(0, TextLiteralContext);
    }
...
}

And because neither of _option and _security are initialized, this produces the error mentioned in the title.

A workaround is to set "strictPropertyInitialization" in tsconfig.json to false, but I would prefer that antlr4ts makes these class members optional, like:

export class RoutineOptionContext extends ParserRuleContext {
    public _option: Token | undefined;
    public _security: Token | undefined;
    public textLiteral(): TextLiteralContext | undefined {
        return this.tryGetRuleContext(0, TextLiteralContext);
    }
...
}

Side note: the choice to prefix all such label variables with an underscore is, at least, questionable. They are not private and the convention is that only private variables are written that way (actually, I never use an underscore in my fields/variables).

ivosh commented 3 years ago

I encountered this problem as well. We use "strict: true" option in tsconfig.json in our project.

I considered various workarounds and at the end decided to:

sharwell commented 3 years ago

... but I would prefer that antlr4ts makes these class members optional ...

This is definitely not a fix. Significant effort (e.g. https://github.com/antlr/antlr4/pull/1576, https://github.com/antlr/antlr4/pull/1582) went into ensuring that non-optional properties in the rule were generated as non-optional properties in the grammar. For the example above, _option is only undefined as a non-observable transient state. When routineOption() returns without an error, _option will be a valid Token.

Is there another way to suppress this warning for members that will be initialized before the type is used, but are not initialized in the constructor?

sharwell commented 3 years ago

Looks like we can add a definite assignment assertion to the property:

public _option!: Token;
mike-lischke commented 3 years ago

But the properties are not always assigned! For instance security is only used in specific alts. In all others it is unassigned and should appear therefore as optional.

sharwell commented 3 years ago

In all others it is unassigned and should appear therefore as optional.

Yes, these should appear as optional.

sharwell commented 3 years ago

Unfortunately, it looks like the TokenDecl and RuleContextDecl models don't currently track an optional property the same way context getters do. The temporary solution is declaring all context properties with a non-null assertion operator, which adheres to the C# Nullable Reference Types practice of treating "oblivious" (or unknown nullability) scenarios as non-null.