diff --git a/frontend/.eslintrc b/frontend/.eslintrc
index c89d89c857..3efd6aea69 100644
--- a/frontend/.eslintrc
+++ b/frontend/.eslintrc
@@ -18,6 +18,8 @@
"i18next/no-literal-string": "error",
"unused-imports/no-unused-imports": "error",
"prettier/prettier": ["error"],
+ // Enforce using optional chaining (?.) instead of && chains for null/undefined checks
+ "@typescript-eslint/prefer-optional-chain": "error",
// Resolves https://stackoverflow.com/questions/59265981/typescript-eslint-missing-file-extension-ts-import-extensions/59268871#59268871
"import/extensions": [
"error",
diff --git a/frontend/src/components/features/browser/browser.tsx b/frontend/src/components/features/browser/browser.tsx
index 8c3842edd4..95c8f1fa1a 100644
--- a/frontend/src/components/features/browser/browser.tsx
+++ b/frontend/src/components/features/browser/browser.tsx
@@ -12,10 +12,9 @@ export function BrowserPanel() {
reset();
}, [conversationId, reset]);
- const imgSrc =
- screenshotSrc && screenshotSrc.startsWith("data:image/png;base64,")
- ? screenshotSrc
- : `data:image/png;base64,${screenshotSrc || ""}`;
+ const imgSrc = screenshotSrc?.startsWith("data:image/png;base64,")
+ ? screenshotSrc
+ : `data:image/png;base64,${screenshotSrc ?? ""}`;
return (
diff --git a/frontend/src/components/features/chat/event-content-helpers/get-observation-content.ts b/frontend/src/components/features/chat/event-content-helpers/get-observation-content.ts
index 435a686918..11276a4e39 100644
--- a/frontend/src/components/features/chat/event-content-helpers/get-observation-content.ts
+++ b/frontend/src/components/features/chat/event-content-helpers/get-observation-content.ts
@@ -140,7 +140,7 @@ const getTaskTrackingObservationContent = (
content += "\n\n**Task List:** Empty";
}
- if (event.content && event.content.trim()) {
+ if (event.content?.trim()) {
content += `\n\n**Result:** ${event.content.trim()}`;
}
diff --git a/frontend/src/components/features/chat/messages.tsx b/frontend/src/components/features/chat/messages.tsx
index 0d9032164d..6e68089b13 100644
--- a/frontend/src/components/features/chat/messages.tsx
+++ b/frontend/src/components/features/chat/messages.tsx
@@ -192,8 +192,7 @@ export const Messages: React.FC
= React.memo(
) => {
const conversationInstructions = `Target file: ${target}\n\nDescription: ${query}\n\nTriggers: ${triggers.join(", ")}`;
if (
- !conversation ||
- !conversation.selected_repository ||
+ !conversation?.selected_repository ||
!conversation.selected_branch ||
!conversation.git_provider ||
!selectedEventId
diff --git a/frontend/src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx b/frontend/src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx
index fdc9b21b00..c5ab171ca8 100644
--- a/frontend/src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx
+++ b/frontend/src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx
@@ -75,7 +75,7 @@ export function GitProviderDropdown({
}
// If no input value, show all providers
- if (!inputValue || !inputValue.trim()) {
+ if (!inputValue?.trim()) {
return providers;
}
diff --git a/frontend/src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx b/frontend/src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx
index 485f574f79..45b75bbd9f 100644
--- a/frontend/src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx
+++ b/frontend/src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx
@@ -99,7 +99,7 @@ export function GitRepoDropdown({
);
// If no input value, return all recent repos for this provider
- if (!inputValue || !inputValue.trim()) {
+ if (!inputValue?.trim()) {
return providerFilteredRepos;
}
@@ -139,7 +139,7 @@ export function GitRepoDropdown({
baseRepositories = repositories;
}
// If no input value, show all repositories
- else if (!inputValue || !inputValue.trim()) {
+ else if (!inputValue?.trim()) {
baseRepositories = repositories;
}
// For URL inputs, use the processed search input for filtering
@@ -246,8 +246,7 @@ export function GitRepoDropdown({
// Create sticky footer item for GitHub provider
const stickyFooterItem = useMemo(() => {
if (
- !config ||
- !config.APP_SLUG ||
+ !config?.APP_SLUG ||
provider !== ProviderOptions.github ||
config.APP_MODE !== "saas"
)
diff --git a/frontend/src/components/features/home/shared/dropdown-item.tsx b/frontend/src/components/features/home/shared/dropdown-item.tsx
index 08e22dc12b..36a0e25967 100644
--- a/frontend/src/components/features/home/shared/dropdown-item.tsx
+++ b/frontend/src/components/features/home/shared/dropdown-item.tsx
@@ -45,7 +45,7 @@ export function DropdownItem({
// eslint-disable-next-line react/jsx-props-no-spreading
- {renderIcon && renderIcon(item)}
+ {renderIcon?.(item)}
{getDisplayText(item)}
diff --git a/frontend/src/components/v1/chat/event-content-helpers/parse-message-from-event.ts b/frontend/src/components/v1/chat/event-content-helpers/parse-message-from-event.ts
index 17824a51c8..8e2a0cb253 100644
--- a/frontend/src/components/v1/chat/event-content-helpers/parse-message-from-event.ts
+++ b/frontend/src/components/v1/chat/event-content-helpers/parse-message-from-event.ts
@@ -5,7 +5,7 @@ export const parseMessageFromEvent = (event: MessageEvent): string => {
const message = event.llm_message;
// Safety check: ensure llm_message exists and has content
- if (!message || !message.content) {
+ if (!message?.content) {
return "";
}
diff --git a/frontend/src/mocks/secrets-handlers.ts b/frontend/src/mocks/secrets-handlers.ts
index 3d5570943a..18c4dc98fd 100644
--- a/frontend/src/mocks/secrets-handlers.ts
+++ b/frontend/src/mocks/secrets-handlers.ts
@@ -34,7 +34,7 @@ export const SECRETS_HANDLERS = [
http.post("/api/secrets", async ({ request }) => {
const body = (await request.json()) as CustomSecret;
- if (typeof body === "object" && body && body.name) {
+ if (typeof body === "object" && body?.name) {
secrets.set(body.name, body);
return HttpResponse.json(true);
}
@@ -48,7 +48,7 @@ export const SECRETS_HANDLERS = [
if (typeof id === "string" && typeof body === "object") {
const secret = secrets.get(id);
- if (secret && body && body.name) {
+ if (secret && body?.name) {
const newSecret: CustomSecret = { ...secret, ...body };
secrets.delete(id);
secrets.set(body.name, newSecret);
diff --git a/frontend/src/mocks/settings-handlers.ts b/frontend/src/mocks/settings-handlers.ts
index c08cd8dc36..e0d7b1ed11 100644
--- a/frontend/src/mocks/settings-handlers.ts
+++ b/frontend/src/mocks/settings-handlers.ts
@@ -134,7 +134,7 @@ export const SETTINGS_HANDLERS = [
const providerTokensSet: Partial> =
Object.fromEntries(
Object.entries(rawTokens)
- .filter(([, val]) => val && val.token)
+ .filter(([, val]) => val?.token)
.map(([provider]) => [provider as Provider, ""]),
);
diff --git a/frontend/src/routes/vscode-tab.tsx b/frontend/src/routes/vscode-tab.tsx
index 0d64180c1d..e1bb2e8fe4 100644
--- a/frontend/src/routes/vscode-tab.tsx
+++ b/frontend/src/routes/vscode-tab.tsx
@@ -51,7 +51,7 @@ function VSCodeTab() {
);
}
- if (error || (data && data.error) || !data?.url || iframeError) {
+ if (error || data?.error || !data?.url || iframeError) {
return (
{iframeError ||
diff --git a/frontend/src/utils/extract-model-and-provider.ts b/frontend/src/utils/extract-model-and-provider.ts
index 93ef12d8bf..ab0836079f 100644
--- a/frontend/src/utils/extract-model-and-provider.ts
+++ b/frontend/src/utils/extract-model-and-provider.ts
@@ -16,7 +16,7 @@ import {
* splitIsActuallyVersion(split) // returns true
*/
const splitIsActuallyVersion = (split: string[]) =>
- split[1] && split[1][0] && isNumber(split[1][0]);
+ split[1]?.[0] && isNumber(split[1][0]);
/**
* Given a model string, extract the provider and model name. Currently the supported separators are "/" and "."