Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Definite assignment assertions #20166

Merged
merged 7 commits into from
Nov 21, 2017
Merged

Conversation

ahejlsberg
Copy link
Member

This PR introduces the ability for a variable or property declaration to include a definite assignment assertion in the form of a ! character following the variable or property identifier. A definite assignment assertion instructs the control flow analyzer to always consider the variable definitely assigned, even when the analyzer is unable to prove it.

// Compile with --strict
class C {
    data!: string;  // Suppress strict initialization check
    setData(value: string) {
        this.data = value;
    }
    getData(): string {
        return this.data;
    }
}

In the example above, if it is known that setData will always be called before getData, there is no need to initialize the data property upon construction. However, the control flow analyzer can't make that assumption, and in --strictPropertyInitialization mode (see #20075) it reports an error unless a definite assignment assertion ! is included.

function f() {
    let x!: number;  // Suppress definite assignment check
    doSomethingWithCallback(() => {
        x = 1;
    });
    console.log(x);
}

In the example above, if it is known that doSomethingWithCallback will always invoke the callback before returning, there is no need to initialize x in its declaration. However, the control flow analyzer conservatively assumes that x is used before being assigned in the console.log(x) call and reports an error. A definite assignment assertion can now be included in the declaration to suppress this error.

Definite assignment assertions are permitted only in the following cases:

  • On a property declaration in a class, provided the property has a type annotation and no initializer and isn't static or abstract.
  • On a let or var variable declaration, provided the variable has a type annotation and no initializer.

Related issues: #11463, #12855, #13811.

Copy link
Contributor

@mhegazy mhegazy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will need some tests for the new token

@ahejlsberg ahejlsberg added this to the TypeScript 2.7 milestone Nov 20, 2017
@ahejlsberg ahejlsberg merged commit 9abb72d into master Nov 21, 2017
@ahejlsberg ahejlsberg deleted the definiteAssignmentAssertions branch November 21, 2017 14:31
@mihailik
Copy link
Contributor

@ahejlsberg what's the best way to add suppression at call site, not globally for the variable?

See your second example, like this:

function f() {
    let x: number;  <---- reluctant to suppress checks for real code   
    doSomethingWithCallback(() => {
        x = 1;
    });
    console.log( x as x! ); <----- prefer to suppress checks at diagnostic call site only 
}

@gcnew
Copy link
Contributor

gcnew commented Nov 21, 2017

@mihailik I think you are looking for the pre-existing not-null assertion:

console.log( x! )

@mihailik
Copy link
Contributor

@gcnew great, but would that work in this case?

@gcnew
Copy link
Contributor

gcnew commented Nov 21, 2017

@mihailik I tested it before posting just to make sure and it worked.

Thinking about it for a second time, is your intention to mark properties as "assigned" in a class constructor under --strictPropertyInitialization? If that's the case, I'm afraid this will not assure the compiler that the property has been assigned. I haven't tried it yet, though.

@mihailik
Copy link
Contributor

@gcnew I was referring to the second example in the original post. It's console.log used as one-off diagnostics, where in-place assert is preferable to the wide-scoped change.

Thanks for the syntax!!

@SlurpTheo
Copy link

Any chance this feature could expand a bit to solve ~A2 as well?

image

.ts(616,8): error TS1255: A definite assignment assertion '!' is not permitted in this context.
.ts(621,14): error TS2365: Operator '!==' cannot be applied to types '0' and '1'.
The terminal process terminated with exit code: 1

I know, I know... ~#9998?

Wish: Instead of the TS1255 error when variable declaration has an initial value, TypeScript treats it as a: don't overly-narrow type for later CFA assertion.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants