-
Notifications
You must be signed in to change notification settings - Fork 22
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
#6526: Make more background APIs strict (strictNullChecks) #7667
Conversation
@@ -50,11 +51,11 @@ export type UserData = Partial<{ | |||
/** | |||
* The user's primary organization. | |||
*/ | |||
organizationId: UUID; | |||
organizationId: UUID | null; |
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.
These types are already inside a Partial<>
that set them to X | undefined
, so this effectively doesn't change anything at runtime.
/** | ||
* The partner principals. Currently, just the Automation Anywhere Control Room principal if applicable. | ||
* @since 1.7.16 | ||
*/ | ||
readonly partnerPrincipals?: Me["partner_principals"]; | ||
readonly partnerPrincipals: Me["partner_principals"]; |
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.
Already part of Partial. ?
is implied
organizationId: organization?.id, | ||
telemetryOrganizationId: telemetryOrganization?.id, | ||
organizationId: organization?.id ?? null, | ||
telemetryOrganizationId: telemetryOrganization?.id ?? null, |
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.
Could not figure out why undefined
is not accepted in this place, so I just made null
explicit.
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.
UserDataUpdate
wraps UserData
in Required<>
. Apparently that doesn't just strip ?
but also prevents setting the var to undefined
while still allowing null
so long as null
is included in the typing. I created a simplified reproduction in TS Playground to confirm.
Given:
type Foo = {
a?: string | null | undefined;
b?: number;
}
type Bar = Required<Foo>;
This throws a ts error:
const baz: Bar = {
a: undefined,
b: 0,
}
This doesn't:
const baz: Bar = {
a: null,
b: 0,
}
TIL
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.
Good find. Indeed, because "null
is a value", so "baz.a
is set"
So our use of Required
is meaningless here, i.e. ?? null
is only hiding the NPE.
We'll have to use a stricter version of Required
:
Will mention this in #7725
@@ -160,7 +160,7 @@ export async function isLinked(): Promise<boolean> { | |||
* Return non-sensitive PixieBrix user profile data. | |||
* @see getExtensionAuth | |||
*/ | |||
export async function getUserData(): Promise<Partial<UserData>> { | |||
export async function getUserData(): Promise<UserData> { |
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.
UserData
is already Partial
, so I made this change everywhere else.
src/background/deploymentUpdater.ts
Outdated
@@ -18,7 +18,7 @@ | |||
import { type Deployment, type Me } from "@/types/contract"; | |||
import { isEmpty, partition } from "lodash"; | |||
import reportError from "@/telemetry/reportError"; | |||
import { getUID } from "@/background/messenger/api"; | |||
import { getUID } from "@/telemetry/telemetryHelpers"; |
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.
No need to go through messenger. Allow direct access of getUID
from anywhere since it's storage-based. Currently it's only called by the background page and the deployments page (which probably won't be reached before this is generated and stored)
import reportEvent from "@/telemetry/reportEvent"; | ||
import { initTelemetry } from "@/background/telemetry"; | ||
import { getUID } from "@/background/messenger/api"; | ||
import { initTelemetry, recordEvent } from "@/background/telemetry"; |
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.
No need to go through messenger. Allow direct access for recordEvent
from the background.
import { | ||
ensureContextMenu, | ||
preloadContextMenus, | ||
uninstallContextMenu, | ||
} from "@/background/contextMenus"; | ||
} from "@/background/contextMenus"; // 300 errors |
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 added some comments to make it easier to tell which files will take more time to make strict and not waste further time trying and trying again.
The lines with "messenger import" means that they will cause another context's Messenger registration to be required, so they'd have to be fixed first.
@@ -150,7 +151,7 @@ export type OrganizationAuthState = { | |||
/** | |||
* The package scope of the organization, or null if not set. | |||
*/ | |||
readonly scope?: string; | |||
readonly scope?: string | null; |
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 I've brought this up before, but I don't remember what we decided.
Functionally type foo?: string | null
is different from type foo: string | null | undefined
. (<key> in <object>
behaves differently for instance)
Do we think it's worth the effort to enforce the difference with type checking? There's a tsconfig option: https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes
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 can try enabling exactOptionalPropertyTypes
in a future PR if you'd like to see what that entails. There are "only" 100 errors right now, and it only applies to the strictNullCheck'd files.
Functionally
type foo?: string | null
is different fromtype foo: string | null | undefined
. (<key> in <object>
behaves differently for instance)
I don't particularly care as long as TypeScript handles it correctly (i.e. after key in object
, object.key
can still be undefined | null
).
object.key
or object.key == null
already covers regular null checking, whereas key in object
should only be needed in type guards, e.g. if key in obj, then obj is Foo
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.
The only reason I really think it might be useful is because we've decided to try to treat null
and undefined
interchangeably as much as possible (hence Nullishable
and foo == null
vs foo === null
).
I can't think of a time it's hurt us not being explicit in the differences. But if it's simple to enforce the distinction, it could potentially make our type guards safer. If it's a pain to implement, I would rather disregard.
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.
src/auth/authUtils.ts
Outdated
@@ -116,5 +116,5 @@ export function selectExtensionAuthState({ | |||
*/ | |||
export async function flagOn(flag: string): Promise<boolean> { | |||
const authData = await readAuthData(); | |||
return authData.flags?.includes(flag); | |||
return Boolean(authData.flags?.includes(flag)); |
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'm curious if this is better than
authData.flags?.includes(flag) ?? false
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.
Yeah I like ?? false
more. Fixed.
@@ -224,8 +223,11 @@ export async function handleInstall({ | |||
const { version } = browser.runtime.getManifest(); | |||
|
|||
if (reason === "install") { | |||
reportEvent(Events.PIXIEBRIX_INSTALL, { | |||
version, | |||
void recordEvent({ |
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.
Is recordEvent
wrongly deprecated? Should we remove the deprecation and instead prevent it from being imported outside the background script/service worker?
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.
prevent it from being imported outside the background script/service worker?
That's what noCrossBoundaryImports
does but that's not a guarantee due the many many exceptions: pixiebrix/eslint-config-pixiebrix#193 (comment)
All it takes is a single import in @/bricks
and it appears in every bundle.
The deprecation makes it "painful" to use this function so you're forced to read its JSDoc. I just updated it to:
/**
* @deprecate Only allowed in @/background files. Otherwise use: `import reportEvent from "@/telemetry/reportEvent"`
*/
If you don't like it at all I can revert the changes to this file and have it go through the messenger anyway.
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.
No, I think your explanation is enough. Just wanted to make sure. We've used deprecations in other places to flag usages we want to warn against even though we don't intend to ever remove the function or type. I think this is a valid example as well.
// Use this instead: `import reportError from "@/telemetry/reportError"` | ||
// export const recordError = getNotifier("RECORD_ERROR", bg); |
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.
// Use this instead: `import reportError from "@/telemetry/reportError"` | |
// export const recordError = getNotifier("RECORD_ERROR", bg); |
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'd rather keep it there so that nobody thinks we just forgot to add the method. Keeping the full export
line also makes it appear when grepping RECORD_ERROR
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7667 +/- ##
==========================================
- Coverage 72.16% 72.13% -0.04%
==========================================
Files 1267 1267
Lines 39691 39701 +10
Branches 7359 7368 +9
==========================================
- Hits 28642 28637 -5
- Misses 11049 11064 +15 ☔ View full report in Codecov by Sentry. |
No loom links were found in the first post. Please add one there if you'd like to it to appear on Slack. Do not edit this comment manually. |
Co-authored-by: Graham Langford <[email protected]>
What does this PR do?
Checklist