-
-
Notifications
You must be signed in to change notification settings - Fork 10
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
Remove as
assertions, or fix to mitigate the risk of silent failure
#158
Conversation
- `as` assertions intended to exclude an empty/nullable type can be replaced by a nullish coalescing operator converting nullable values into an acceptable empty value that doesn't pollute the variable's type signature. - TODO: document and expound in MetaMask/contributor-docs#47
Sometimes TypeScript's type narrowing and inference doesn't fully work quite like it should. Even in these cases, using `as` assertions should be avoided. It may seem like we know for certain what the type should be and there's no risk of it breaking, but there's always the possibility that some combination of changes elsewhere might affect the typing, in which case `as` will suppress the alerts TypeScript would otherwise provide us with. This may lead to code breaking silently. In this case, adding type guards and null checks -- even if they're redundant -- is preferable to the above scenario. - TODO: document and expound in MetaMask/contributor-docs#47
If anything changes in the typing of `ChangeCategory` or `unreleasedChanges` that make them inconsistent, this code will fail silently. Relying on type inference instead of explicit type casting will make these line a little less brittle. - TODO: document and expound in MetaMask/contributor-docs#47
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.
One suggestion here that could remove all of the typecasts, but it is non-blocking. Thanks for doing this.
src/update-changelog.ts
Outdated
// Typecast: currentVersion will be defined here due to type guard at the | ||
// top of this function. | ||
changelog.addRelease({ version: currentVersion as Version }); | ||
changelog.addRelease({ version: currentVersion }); |
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.
This comment is referring to this code at the top of the function:
if (isReleaseCandidate && !currentVersion) {
throw new Error(
`A version must be specified if 'isReleaseCandidate' is set.`,
);
}
Since we're checking for isReleaseCandidate
here, there's no way that currentVersion
is not set. Hence why I believe that the typecast was added originally. (Why TypeScript doesn't see this, I'm not sure.)
This change doesn't hurt, but I wanted to make sure you saw that.
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.
I consolidated the conditional branches so that we can just rely on type narrowing here: 033cd4e. The tradeoff is that the errors now throw a bit later than they strictly should, but the overhead from that seems minimal.
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.
I think with these changes we are now always shelling out a couple of times to gather commit hashes and commits regardless of the arguments given. I gather that the current changes were motivated by principle, that generally, if there are invalid inputs, the user wants to know about it right away rather than have to wait a bit. I agree, though, that this doesn't matter, at least right now, so these changes seem fine.
…branches - more consistent logic for `isReleaseCandidate` null check - wasted processing overhead due to errors throwing later than strictly necessary
- removes need for type casting while iterating over object keys
src/update-changelog.ts
Outdated
} | ||
if (mostRecentTag === `${tagPrefixes[0]}${currentVersion ?? ''}`) { | ||
throw new Error( | ||
`Current version already has tag, which is unexpected for a release candidate.`, |
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.
how about, adding the tag value part of the error message?
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.
You mean something like this?
`Current version already has tag, which is unexpected for a release candidate.`, | |
`Current version already has a tag: '${mostRecentTag}'. This is unexpected for a release candidate.`, |
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.
@kanthesha Let me know if a26b0bb looks good!
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.
LGTM!
- Was added as devDependency while importing in runtime in MetaMask#158.
- Was added as devDependency while importing in runtime in #158.
- `updateChangelog` returns results even if `isReleaseCandidate` is false
- `updateChangelog` returns results even if `isReleaseCandidate` is false
- `updateChangelog` returns results even if `isReleaseCandidate` is false
- `updateChangelog` returns results even if `isReleaseCandidate` is false
## Motivation #158 incorrectly refactored the branches in updateChangelog, resulting in a state where new changelog entries weren't added to the return value if isReleaseCandidate is false. ## Explanation - This error is fixed by moving the logic for retrieving new entries out of the if (isReleaseCandidate) block. - The code for retrieving new entries is also extracted into its own method: getNewChangeEntries, abstracting away details that obscured the flow of the method's core logic. - To verify that the buggy logic is correctly restored, it's necessary to compare the current state of updateChangelog to its state before the bug was introduced. For this, see: [diff link](https://github.com/MetaMask/auto-changelog/compare/e8df1ec717f534c8fe84c46ea86a847fa5a32973..da39a58a55571cf4e8a03e28096aa12c02c75f77#diff-4228e8302e41dd1c51e813af8368efdcb40b3d9db3e3f21f417ae87275d9f389R249-R316). ## References - Closes #180 - Followed by #188
Explanation
as T[]
assertions with?? []
as
assertions intended to exclude an empty/nullable type can be replaced by a nullish coalescing operator converting nullable values into an acceptable empty value that doesn't pollute the variable's type signature.as
assertions with redundant type guardsSometimes TypeScript's type narrowing and inference doesn't fully work quite like it should. Even in these cases, using
as
assertions should be avoided.It may seem like we know for certain what the type should be and there's no risk of it breaking, but there's always the possibility that some combination of changes elsewhere might affect the typing, in which case
as
will suppress the alerts TypeScript would otherwise provide us with. This may lead to code breaking silently.In this example, adding type guards and null checks -- even if they're redundant -- is a small cost to avoid silent failure.
as
assertion rely on inference instead of hard-coded type castingIf anything changes in the typing of
ChangeCategory
orunreleasedChanges
that make them inconsistent, this code will fail silently. Relying on type inference instead of explicit type casting will make this logic a little less brittle.References
any
,as
usage in TypeScript Guidelines doc contributor-docs#47