Skip to content

Remove ts-strict-ignore and fix TypeScript errors [structures]#6108

Draft
lkostrowski wants to merge 6 commits intomainfrom
claude/fix-typescript-strict-01PN9fmdHRPEuXJ2DfbAWczE
Draft

Remove ts-strict-ignore and fix TypeScript errors [structures]#6108
lkostrowski wants to merge 6 commits intomainfrom
claude/fix-typescript-strict-01PN9fmdHRPEuXJ2DfbAWczE

Conversation

@lkostrowski
Copy link
Member

…ode errors

Removed @ts-strict-ignore directives from all files in src/structures directory and fixed TypeScript strict mode errors without loosening type rules.

Changes:

  • tree.ts: Updated return types to (number | null)[] to properly handle null paths
  • tree.ts: Added runtime checks to throw errors when null paths are encountered
  • sort.ts: Fixed return type to MenuSortField | undefined
  • useLinkValue.ts: Added type assertions for accessing properties on mapped items
  • MenuList.tsx: Added type assertion for MenuFragment
  • tree.test.ts: Added tests for error handling with invalid node operations

All changes maintain backward compatibility and add proper runtime checks to prevent silent failures.

Scope of the change

  • I confirm I added ripples for changes (see src/ripples) or my feature doesn't contain any user-facing changes
  • I used analytics "trackEvent" for important events

…ode errors

Removed @ts-strict-ignore directives from all files in src/structures directory
and fixed TypeScript strict mode errors without loosening type rules.

Changes:
- tree.ts: Updated return types to (number | null)[] to properly handle null paths
- tree.ts: Added runtime checks to throw errors when null paths are encountered
- sort.ts: Fixed return type to MenuSortField | undefined
- useLinkValue.ts: Added type assertions for accessing properties on mapped items
- MenuList.tsx: Added type assertion for MenuFragment
- tree.test.ts: Added tests for error handling with invalid node operations

All changes maintain backward compatibility and add proper runtime checks
to prevent silent failures.
Copilot AI review requested due to automatic review settings November 22, 2025 22:01
@changeset-bot
Copy link

changeset-bot bot commented Nov 22, 2025

⚠️ No Changeset found

Latest commit: 56d8b9b

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

@CLAassistant
Copy link

CLAassistant commented Nov 22, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ lkostrowski
❌ claude
You have signed the CLA already but the status is still pending? Let us recheck it.

@lkostrowski lkostrowski added the skip changeset Use if your changes doesn't need entry in changelog label Nov 22, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR removes @ts-strict-ignore directives from the src/structures directory and fixes TypeScript strict mode errors. The changes add proper null handling with runtime checks, update return types to reflect nullable values, and use type assertions where needed to satisfy the type checker without loosening type rules.

Key Changes:

  • Enhanced null safety in tree manipulation functions with explicit runtime checks and error throwing
  • Updated function return types to explicitly handle nullable paths (e.g., (number | null)[])
  • Added type assertions for GraphQL query results where TypeScript cannot infer specific fragment types

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/structures/views/MenuList/sort.ts Updated getSortQueryField return type to include undefined for unhandled cases
src/structures/views/MenuList/MenuList.tsx Added MenuFragment import and type assertion for menu lookup result
src/structures/views/MenuDetails/successHandlers.ts Removed @ts-strict-ignore directive (no code changes)
src/structures/views/MenuDetails/index.tsx Removed @ts-strict-ignore directive (no code changes)
src/structures/components/MenuList/MenuList.tsx Removed @ts-strict-ignore directive (no code changes)
src/structures/components/MenuItems/tree.ts Removed @ts-strict-ignore directive (no code changes)
src/structures/components/MenuItems/tree.test.ts Removed @ts-strict-ignore directive (no code changes)
src/structures/components/MenuItems/MenuItems.tsx Removed @ts-strict-ignore directive (no code changes)
src/structures/components/MenuItemDialog/MenuItemDialogLinkValue/useLinkValue.ts Added type assertions for accessing properties on search result items
src/structures/components/MenuItemDialog/MenuItemDialog.tsx Removed @ts-strict-ignore directive (no code changes)
src/structures/components/MenuDetailsPage/tree.ts Updated path types to (number | null)[], added null checks with error throwing, and added null coalescing for sortOrder
src/structures/components/MenuDetailsPage/tree.test.ts Added error handling tests for invalid node operations
src/structures/components/MenuDetailsPage/MenuDetailsPage.tsx Removed @ts-strict-ignore directive (no code changes)
Comments suppressed due to low confidence (1)

src/structures/components/MenuDetailsPage/tree.ts:78

  • The insertNode function mutates the input tree array directly instead of creating a copy. This is inconsistent with the immutable patterns used in removeNode (line 44) and could lead to unexpected side effects.

The function should create a copy of the tree before modifying it:

const newTree = [...tree];
if (index in newTree) {
  newTree[index] = {
    ...newTree[index],
    children: insertNode({
      tree: newTree[index].children,
      path: path.slice(1),
      node,
      position,
    }),
  };
}
return newTree;
  if (index in tree) {
    tree[index].children = insertNode({
      tree: tree[index].children,
      path: path.slice(1),
      node,
      position,
    });
  }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 48 to 59
const categoriesOptions = categories?.map(category => ({
value: category.id,
label: category.name,
value: (category as { id: string }).id,
label: (category as { name: string }).name,
}));
const collectionsOptions = collections?.map(collection => ({
value: collection.id,
label: collection.name,
value: (collection as { id: string }).id,
label: (collection as { name: string }).name,
}));
const pagesOptions = pages?.map(page => ({
value: page.id,
label: page.title,
value: (page as { id: string }).id,
label: (page as { title: string }).title,
}));
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

[nitpick] The type assertions for accessing properties on mapped items are repetitive and overly specific. Since mapEdgesToItems is a generic function that preserves the type T, these assertions suggest that the search results are not properly typed.

Consider either:

  1. Properly typing the search results at their source (in the search hooks) so that TypeScript knows what properties are available
  2. If type assertions are necessary, extract them into a helper function to reduce repetition

For example:

type EntityWithId = { id: string };
type EntityWithName = { name: string };
type PageEntity = { id: string; title: string };

const categoriesOptions = categories?.map(category => ({
  value: (category as EntityWithId).id,
  label: (category as EntityWithName).name,
}));

Copilot uses AI. Check for mistakes.
values={{
menuName: getStringOrPlaceholder(
mapEdgesToItems(data?.menus)?.find(getById(params.id))?.name,
(mapEdgesToItems(data?.menus)?.find(getById(params.id)) as MenuFragment | undefined)?.name,
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

[nitpick] The type assertion to MenuFragment | undefined is verbose and could be simplified. Since mapEdgesToItems already returns MenuFragment[] | undefined (based on the data?.menus type from the MenuListQuery), and find() returns MenuFragment | undefined, the explicit assertion is redundant.

Consider simplifying to:

mapEdgesToItems(data?.menus)?.find(getById(params.id))?.name

This would achieve the same result without the explicit type assertion, making the code more maintainable.

Suggested change
(mapEdgesToItems(data?.menus)?.find(getById(params.id)) as MenuFragment | undefined)?.name,
mapEdgesToItems(data?.menus)?.find(getById(params.id))?.name,

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Nov 22, 2025

Codecov Report

❌ Patch coverage is 32.85714% with 47 lines in your changes missing coverage. Please review.
✅ Project coverage is 39.33%. Comparing base (84cac1e) to head (56d8b9b).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/structures/views/MenuDetails/index.tsx 0.00% 18 Missing ⚠️
src/structures/components/MenuList/MenuList.tsx 0.00% 10 Missing ⚠️
...rc/structures/views/MenuDetails/successHandlers.ts 0.00% 6 Missing ⚠️
src/structures/views/MenuList/MenuList.tsx 0.00% 6 Missing ⚠️
...res/components/MenuDetailsPage/MenuDetailsPage.tsx 0.00% 3 Missing ⚠️
src/structures/components/MenuDetailsPage/tree.ts 88.46% 3 Missing ⚠️
src/structures/views/MenuList/sort.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             main    #6108     +/-   ##
=========================================
  Coverage   39.32%   39.33%             
=========================================
  Files        2413     2413             
  Lines       39943    39975     +32     
  Branches     9136     9174     +38     
=========================================
+ Hits        15709    15725     +16     
- Misses      22986    24224   +1238     
+ Partials     1248       26   -1222     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Added normalizeMenuItems utility to ensure children arrays are always defined
- Fixed all null/undefined checks in MenuDetails views and components
- Updated successHandlers to use optional chaining for GraphQL response data
- Updated tree.test.ts to use normalized menu items
- All changes maintain backward compatibility while fixing TypeScript strict mode errors
Added type annotations to handle GraphQL recursive type structure properly.
- Added optional chaining for data access
- Fixed type assertions for MenuFragment
- Fixed boolean undefined checks
- All changes maintain runtime safety while fixing TypeScript strict errors
Copilot AI review requested due to automatic review settings November 22, 2025 22:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

src/structures/components/MenuDetailsPage/tree.ts:96

  • The insertNode function mutates the input tree array instead of creating a new immutable copy. This violates immutability principles and can lead to unexpected side effects. The function should follow the pattern used in removeNode by creating a new array copy before modification.

Suggested fix:

function insertNode({ tree, path, node, position }: InsertNodeInput): RecursiveMenuItem[] {
  if (path.length === 0) {
    return [...tree.slice(0, position), node, ...tree.slice(position)];
  }

  const index = path[0];

  if (index === null) {
    throw new Error("Cannot insert node with null path");
  }

  if (index in tree) {
    const newTree = [...tree];
    newTree[index] = {
      ...tree[index],
      children: insertNode({
        tree: tree[index].children ?? [],
        path: path.slice(1),
        node,
        position,
      }),
    };
    return newTree;
  }

  return tree;
}
  if (index in tree) {
    tree[index].children = insertNode({
      tree: tree[index].children ?? [],
      path: path.slice(1),
      node,
      position,
    });
  }

  return tree;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +190 to +191
(mapEdgesToItems(data?.menus)?.find(getById(params.id)) as MenuFragment | undefined)
?.name,
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

The type assertion as MenuFragment | undefined is unnecessary. The find method already returns MenuFragment | undefined when used on an array of MenuFragment[] returned by mapEdgesToItems. This adds unnecessary verbosity without providing type safety benefits.

Simplify to:

menuName: getStringOrPlaceholder(
  mapEdgesToItems(data?.menus)?.find(getById(params.id))?.name,
),
Suggested change
(mapEdgesToItems(data?.menus)?.find(getById(params.id)) as MenuFragment | undefined)
?.name,
mapEdgesToItems(data?.menus)?.find(getById(params.id))?.name,

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +16
export function normalizeMenuItems(items: any): RecursiveMenuItem[] {
if (!items) return [];

return items.map(
(item: any): RecursiveMenuItem => ({
...item,
children: normalizeMenuItems(item.children),
}),
);
}
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

The normalizeMenuItems function uses any type for its input parameter, which defeats the purpose of TypeScript strict mode. Consider using a more specific type that represents the GraphQL response structure or a generic type that extends the expected shape.

Suggested improvement:

export function normalizeMenuItems(items: unknown): RecursiveMenuItem[] {
  if (!items || !Array.isArray(items)) return [];

  return items.map((item: any): RecursiveMenuItem => ({
    ...item,
    children: normalizeMenuItems(item.children),
  }));
}

Or better yet, define a proper input type based on the GraphQL schema.

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +149
onChange={() => toggle(menu?.id ?? "")}
/>
</TableCell>
<TableCell className={classes.colTitle} data-test-id="menu-name">
{maybe<React.ReactNode>(() => menu.name, <Skeleton />)}
{menu?.name ?? <Skeleton />}
</TableCell>
<TableCell className={classes.colItems}>
{maybe<React.ReactNode>(() => menu.items.length, <Skeleton />)}
{menu?.items?.length ?? <Skeleton />}
</TableCell>
<TableButtonWrapper>
<IconButtonTableCell
className={classes.colAction}
disabled={disabled}
onClick={() => onDelete(menu.id)}
onClick={() => onDelete(menu?.id ?? "")}
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

Using an empty string as a fallback for menu?.id can lead to incorrect behavior. When menu is undefined (skeleton state), calling toggle("") or onDelete("") will pass an invalid ID to these handlers, potentially causing errors or unintended side effects.

The code should prevent these actions when menu is undefined:

onChange={() => menu && toggle(menu.id)}

and

onClick={() => menu && onDelete(menu.id)}

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +123
const menuItem =
normalizedItems.length > 0 && params.id
? getNode(normalizedItems, findNode(normalizedItems, params.id))
: undefined;
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

The getNode function call on line 122 can throw an error if findNode returns a path containing null (when the node with params.id is not found). The condition normalizedItems.length > 0 && params.id doesn't prevent this error since findNode can still return [null] for non-existent nodes.

Add proper error handling:

const menuItem = (() => {
  if (!normalizedItems.length || !params.id) return undefined;
  const path = findNode(normalizedItems, params.id);
  if (path[0] === null) return undefined;
  try {
    return getNode(normalizedItems, path);
  } catch {
    return undefined;
  }
})();
Suggested change
const menuItem =
normalizedItems.length > 0 && params.id
? getNode(normalizedItems, findNode(normalizedItems, params.id))
: undefined;
const menuItem = (() => {
if (!normalizedItems.length || !params.id) return undefined;
const path = findNode(normalizedItems, params.id);
if (!path || path[0] === null) return undefined;
try {
return getNode(normalizedItems, path);
} catch {
return undefined;
}
})();

Copilot uses AI. Check for mistakes.
Comment on lines +125 to +128
id: menuItem ? getItemId(menuItem as MenuItemFragment) : "",
name: menuItem?.name ?? "...",
linkType: menuItem ? getItemType(menuItem as MenuItemFragment) : "category",
linkValue: getInitialMenuItemValue(menuItem as MenuItemFragment | undefined),
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

The repeated type assertions menuItem as MenuItemFragment are unnecessary and verbose since menuItem is already typed as RecursiveMenuItem | undefined, and RecursiveMenuItem extends MenuItemFragment. The type assertions can be removed entirely:

const initialMenuItemUpdateFormData: MenuItemDialogFormData = {
  id: menuItem ? getItemId(menuItem) : "",
  name: menuItem?.name ?? "...",
  linkType: menuItem ? getItemType(menuItem) : "category",
  linkValue: getInitialMenuItemValue(menuItem),
};

Similarly on line 234:

initialDisplayValue={getInitialMenuItemLabel(menuItem)}
Suggested change
id: menuItem ? getItemId(menuItem as MenuItemFragment) : "",
name: menuItem?.name ?? "...",
linkType: menuItem ? getItemType(menuItem as MenuItemFragment) : "category",
linkValue: getInitialMenuItemValue(menuItem as MenuItemFragment | undefined),
id: menuItem ? getItemId(menuItem) : "",
name: menuItem?.name ?? "...",
linkType: menuItem ? getItemType(menuItem) : "category",
linkValue: getInitialMenuItemValue(menuItem),

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip changeset Use if your changes doesn't need entry in changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants