GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Flag non-nullable functions in `if` statements as errors by jwbay · Pull Request #32802 · microsoft/TypeScript

Conversation

@jwbay

@jwbay jwbay commented Aug 10, 2019

Copy link
Copy Markdown
Contributor

Under --strictNullChecks, we now error on testing non-nullable function types in if statements if they're not called. This is a subset of #9041 as mentioned here: #9041 (comment).

Example:

function onlyErrorsWhenNonNullable(required: () => boolean, optional?: () => boolean) {
    if (required) { // now an error...
    }

    if (required()) { // ...because you probably meant this, which is still ok
    }

    if (optional) { // still ok
    }
}

Note the concern about third-party values coming in without a strictNullChecks context is still valid :

import { ThirdPartyType, create } from 'some-package'

const x: ThirdPartyType = create();

// this may now flag an error because the type definitions for some-package
// don't mark someFunc as nullable when it should be
if (x.someFunc) {
}

Should we make a recommendation here in the error message like casting to a nullable type?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ulrichb, zhongqil, Cristy94, jrumandal, and SlurpTheo reacted with thumbs up emoji ulrichb, zhongqil, and TheDSCPL reacted with heart emoji
All reactions
  • 👍 5 reactions
  • ❤️ 3 reactions
@jwbay
Flag non-nullable values with call expressions in if statements as …
130615a
…errors
@jwbay jwbay mentioned this pull request Aug 10, 2019
Disallow truthiness coercions on known-true/known-false values #9041
Closed
@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Aug 12, 2019

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test this
TypeScript Bot (@typescript-bot) user test this
TypeScript Bot (@typescript-bot) run dt

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Aug 12, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Ryan Cavanaugh (@RyanCavanaugh), I've started to run the parallelized community code test suite on this PR at 130615a. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Aug 12, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Ryan Cavanaugh (@RyanCavanaugh), I've started to run the parallelized Definitely Typed test suite on this PR at 130615a. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Aug 12, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Ryan Cavanaugh (@RyanCavanaugh), I've started to run the extended test suite on this PR at 130615a. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Aug 12, 2019

Copy link
Copy Markdown
Member

There should probably be a special case for if(!!expr) { so that there's an idiomatic non-casting workaround for checkJs scenarios

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot TypeScript Bot (typescript-bot) mentioned this pull request Aug 12, 2019
🤖 User test baselines have changed for testNonNullableCallSignatures jwbay/TypeScript#1
Open
@typescript-bot

TypeScript Bot (typescript-bot) commented Aug 12, 2019

Copy link
Copy Markdown
Contributor

The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@falsandtru

falsandtru (falsandtru) commented Aug 13, 2019

Copy link
Copy Markdown
Contributor

I don't think this is the scope of strictNullChecks. This change injures the certainty and reliance of strictNullChecks. This change should be enabled by a new flag.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Aug 16, 2019

Copy link
Copy Markdown
Member

This didn't go nearly as well as expected, unfortunately.

RWC turned up over a hundred examples of code of the form

if (someExpr) {
    someExpr();
}

where someExpr isn't really provably not-undefined because:

  • It was a class property not covered by strictPropertyInitialization
  • It was an array or map property lookup, this might be undefined even though we generally pretend this never happens
  • It's a DOM property that isn't present in all browsers, so it's valid feature-detection code
  • It comes from props that might have bad JS callers (this one is more questionable)

Some of this also probably happened as codebases started out without strictNullChecks on, wrote code correctly to defensively handle undefineds, then turned SNC on and it just so happened that there weren't identifiable undefineds manifest in those positions after all.

However, there were a few hits some code that didn't call someExpr where it did appear to be a bug:

                if (this.isComponentMounted) {
                    ~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2774: This condition will always return true since the function is always defined. Did you mean to call it instead?
                    this.setState({ isUnblocking: false });
                }

Further restricting the check to functions returning boolean might make the false positive : true positive ratio good enough to be acceptable.

jwbay reacted with eyes emoji
All reactions
  • 👀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

RyanCavanaugh
Ryan Cavanaugh (RyanCavanaugh) requested changes Aug 16, 2019
View reviewed changes

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Let's try with the "functions returning boolean only" approach and hope for good results

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

jwbay reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) self-assigned this Aug 16, 2019
@jwbay
jwbay force-pushed the testNonNullableCallSignatures branch 2 times, most recently from 3906a1e to e81f5ec Compare August 18, 2019 17:59
@jwbay

jwbay commented Aug 18, 2019

Copy link
Copy Markdown
Contributor Author

Let's try with the "functions returning boolean only" approach and hope for good results

Added a commit for this 🤞

I imagine one way to achieve a far better signal-to-noise ratio would be checking syntax to see if and how the thing being tested is used inside the block body.

if (this.isComponentMounted) {
    // definitely suspicious because `isComponentMounted` isn't used in the block
    this.setState({ isUnblocking: false });
}

if (this.isComponentMounted) {
    // but probably ok if it's called in the block
    this.setState({ isUnblocking: this.isComponentMounted() });

    // or is passed to something else
    this.setState({ isUnblocking: someCheck(this.isComponentMounted) });
}

However... I'm guessing a brute-force syntax walk over arbitrarily deep children would be too expensive to perform here. Is there some way to attach information like this during an earlier syntax walk, like I imagine might be done for scope checking or CFA?

There should probably be a special case for if(!!expr) { so that there's an idiomatic non-casting workaround for checkJs scenarios

This happens to already work due to the way we're checking specific syntax kinds. Added a test for it

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jwbay
Only error when testing functions that return booleans
327ff39
@jwbay
jwbay force-pushed the testNonNullableCallSignatures branch from e81f5ec to 327ff39 Compare August 18, 2019 18:56
@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Aug 19, 2019

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test this
TypeScript Bot (@typescript-bot) user test this

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Aug 19, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Ryan Cavanaugh (@RyanCavanaugh), I've started to run the extended test suite on this PR at 327ff39. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Aug 19, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Ryan Cavanaugh (@RyanCavanaugh), I've started to run the parallelized community code test suite on this PR at 327ff39. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Aug 19, 2019

Copy link
Copy Markdown
Member

Latest commit has one false positive (the event handler happened to return boolean) and three (!) instances of

if (this.isComponentMounted) {
  ...
}

which is pretty good IMO.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Aug 19, 2019
Suggestion Backlog Slog, 8/19/2019 #32981
Closed
@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Aug 19, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

jwbay re: tree walks!

We talked about this more and actually think a tree walk of the body of an if could be a plausibly-cheap thing to do in this particular case. Observations:

  • The check is extremely scoped, so wouldn't fire very often
  • 98%+ of the time when an error wouldn't be issued, the subsequent call will be very early in a depth-first check, so the walk can bail before covering too much
  • The other 1% of the time when we do end up erroring, well, erroring compilations are allowed to be slow
  • The very small proportion of cases where we do walk the entire if body to find the call "late" in the tree is rare enough to not be a meaningful perf hit

This would allow TS to check this for any function, not just those returning boolean, and would eliminate one of the false positives we had.

So if it's OK with you, we'd like to compare-and-contrast the results from the boolean-test PR with maybe a fresh PR that does do the walk? Would you be game to implement both ways so we can see which has the best yield?

Thanks again for your work on this so far; I think this is going to be a high-value check either way.

jwbay reacted with thumbs up emoji jwbay reacted with heart emoji
All reactions
  • 👍 1 reaction
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jwbay jwbay mentioned this pull request Aug 31, 2019
Flag non-nullable functions in if statements as errors (tree walk version) #33178
Merged
@falsandtru

falsandtru (falsandtru) commented Sep 2, 2019

Copy link
Copy Markdown
Contributor

I agree with the new approach #33178. This approach should be adopted. However, I still this is not null checks. This check should be called like strictCallChecks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@orta

Orta Therox (orta) commented Sep 25, 2019

Copy link
Copy Markdown
Contributor

Closing as #33178 is now merged

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@orta Orta Therox (orta) closed this Sep 25, 2019
@ssalbdivad David Blass (ssalbdivad) mentioned this pull request Jan 7, 2020
Apply uncalled function checks to ternaries #36048
Closed
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Jun 17, 2020
Incorrect "did you mean to call" error issued for invoked class property #38161
Closed
@luryson

luryson commented Nov 17, 2021

Copy link
Copy Markdown

can't under stand why if (true) will report TS2774, but if (false) reports nothing.

interface A {
        a: () => boolean;
    }

    class AImpl implements A {
        public a(): boolean {
            return true;
        }
    }

    it('should fail', function () {
        const aImpl = new AImpl();
        if (aImpl.a) { // TS2774: This condition will always return true since this function is always defined. Did you mean to call it instead?
            console.log("always true");
        }
    });

    it('why success', function () {
        const aImpl = new AImpl();
        if (!aImpl.a) { // nothing happens here
            console.log('always false');
        }
    });
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
@typescript-bot

TypeScript Bot (typescript-bot) commented Oct 21, 2025

Copy link
Copy Markdown
Contributor

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) RyanCavanaugh requested changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh)

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

6 participants

@jwbay @RyanCavanaugh @typescript-bot @falsandtru @orta @luryson
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.