Skip to content

Add strict null checks to the codebase#233

Closed
petebacondarwin wants to merge 4 commits intocloudflare:mainfrom
petebacondarwin:strict-null-checks
Closed

Add strict null checks to the codebase#233
petebacondarwin wants to merge 4 commits intocloudflare:mainfrom
petebacondarwin:strict-null-checks

Conversation

@petebacondarwin
Copy link
Copy Markdown
Contributor

Also converts incorrect use of void to undefined and adds some words to the CSpell dictionary.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Jan 11, 2022

⚠️ No Changeset found

Latest commit: 4b62761

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Comment thread packages/wrangler/pages/functions/filepath-routing.ts Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is a shame that TS cannot infer this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could include in the while check a && Array.isArray(searchPaths) it could be not inferring because .length can also be on a string.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

or alternatively

Suggested change
const cwd = searchPaths.shift()!;
const cwd = Array.isArray(searchPaths) && searchPaths.shift()!;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we run across a lot of these we can make a custom type-guard, this example isn't inferring anything from inputs or using generics.

function isNonEmptyArrStrings(value: any): boolean {
    return Array.isArray(value) && value.length && value.every(item => typeof item === "string");
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm going to add the guard because that is quite cool

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is no need to check the value (according to the types) since it will always be defined.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Always present, but not always truthy. Once the iterator is exhausted, value will be undefined.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Doh! I should have remembered.

So the "proper" way should be:

const {value, done} = handleIterator.next();
if (!done) {
 ...
}

But then that would mean that we don't handle the final return statement from the executeRequest() generator.

So I can see that this is the most appropriate approach. Thanks for catching this.
(If only we had unit tests for this...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Or we could do:

      const context: EventContext<unknown, string, Record<string, unknown>> = {
        request: new Request(request.clone()),
        next: done ? () => {} : next,
        params,
        data,
        env,
        waitUntil: workerContext.waitUntil.bind(workerContext),
      };

But that is a bit less easy to grok, perhaps?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh actually, I remember why we needed to remove this.
If we have the if (value) then we must also have an else clause that returns "something". Otherwise the return type of next() becomes Promise<Response|undefined> which is not compatible with the EventContext type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@GregBrimble - can you take a new look at this file now? I have refactored the code a bit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should this be a named type?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it would clarify the API if it was

Comment thread packages/wrangler/src/dev.tsx Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is possible that the previousBundle is undefined. In this case I have chosen to just return it unchanged. Is that a good idea? cc @threepointone

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would rather assert that it is defined, and throw if not. (In practice it will never be undefined, because this will be called only after the first build) Alternately, a non-null assertion?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll add an error.
But I don't actually understand what the point of this handler is. Is the idea that updating bundle by calling setBundle() causes the Dev component to be re-rendered?

Comment thread packages/wrangler/src/inspect.ts Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is set to wsServerRef.current but not if we use a fat arrow...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

that's strange.

Copy link
Copy Markdown
Contributor

@threepointone threepointone left a comment

Choose a reason for hiding this comment

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

Left a first round of comments

Comment thread packages/wrangler/src/dev.tsx Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would rather assert that it is defined, and throw if not. (In practice it will never be undefined, because this will be called only after the first build) Alternately, a non-null assertion?

Comment thread packages/wrangler/src/index.tsx Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we want to return here, instead we'll have to create the object and copy the inherited fields on to it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The previous code would crash if the value of config.env[key] was undefined, since it had the following unguarded expression: config.env[env] (where in that case env is the key.

Comment thread packages/wrangler/src/index.tsx Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same here, I think we don't want to return early

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Similar to #233 (comment) - the previous code would crash in this case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should it be an error in both cases then?
throw new Error('Missing ${something} in environments)

Comment thread packages/wrangler/src/index.tsx Outdated
Comment thread packages/wrangler/src/index.tsx Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why did you remove the args.local checks here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Two reasons:

  • they are redundant due to the error being thrown earlier in the function if args.local is true
  • if this block is wrapped in an if statement then TS is unable to tell that config.account_id is not undefined in the fetchResult() call below.

Comment thread packages/wrangler/src/index.tsx Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

using <filename> (i.e the pointy brackets) implies demandOption:true, but ok.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That is true for "runtime" but for type checking the typings are only able to indicate that args.filename must not be undefined if the demandOption property is true.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Didn't we recently add the PR from James that allowed for empty filename to attempt to run on a root index? This would override that.

#79

Copy link
Copy Markdown
Contributor Author

@petebacondarwin petebacondarwin Jan 13, 2022

Choose a reason for hiding this comment

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

The PR you link to was replaced by #196.
Even in this new PR the filename option is still required, because that is passed to esbuild as its entry point.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmmm, James had mentioned it defaulting to index the 196 PR will be merged soon and we can check the behavior with this change too

Comment thread packages/wrangler/src/inspect.ts Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should assert here. It's a big problem if it's not defined, so we should probably throw.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

An alternative is that we capture these values in a const outside the callback scope...

      const serverInst = serverRef.current;
      const wsServerInst = wsServerRef.current;
      return () => {
        serverInst.close();
        // Also disconnect any open websockets/devtools connections
        wsServerInst.clients.forEach((ws) => ws.close());
        wsServerInst.close();
      };

But I wasn't sure if we could be confident that this would never change between calls to useEffect() and its cleanup function...

Comment thread packages/wrangler/pages/functions/routes.ts Outdated
Comment thread packages/wrangler/src/pages.tsx Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

seems like we can remove import assert from "assert"; with current changes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

👍

Comment thread packages/wrangler/pages/functions/filepath-routing.ts Outdated
Comment thread packages/wrangler/src/pages.tsx Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
"Trying to fetch assets directly when there is no `directory` option specified, and not in `local` mode."
"Trying to fetch assets directly when there is no `directory` option specified."

@GregBrimble
Copy link
Copy Markdown
Contributor

GregBrimble commented Jan 13, 2022

Tried to add a couple of commits, but realized this is on your fork, @petebacondarwin .

So, two tiny change requests:

--- a/packages/wrangler/src/pages.tsx
+++ b/packages/wrangler/src/pages.tsx
@@ -1,6 +1,5 @@
 /* eslint-disable no-shadow */
 
-import assert from "assert";
 import type { BuilderCallback } from "yargs";
 import { join } from "path";
 import { tmpdir } from "os";
@@ -122,7 +121,9 @@ async function spawnProxyProcess({
       },
     }
   );
-  EXIT_CALLBACKS.push(() => proxy.kill());
+  EXIT_CALLBACKS.push(() => {
+    proxy.kill();
+  });

Other than that, the Pages pieces look good to me! Happy to merge.

@petebacondarwin
Copy link
Copy Markdown
Contributor Author

@GregBrimble as a "collaborator" on this repo, you have the rights to push to this branch on my fork.

Screenshot 2022-01-13 at 14 57 01

`void` should only be used for return types of functions that do not return anything (not even undefined).

See https://www.typescriptlang.org/docs/handbook/2/functions.html#void
The previous fix (removing the `if(value)` check actually broke the behaviour of the code.
@petebacondarwin petebacondarwin deleted the strict-null-checks branch January 19, 2022 13:47
@github-project-automation github-project-automation bot moved this to Untriaged in workers-sdk Sep 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

5 participants