Skip to content

[data grid] Fix getColumn return type not reflecting that it can return undefined#23165

Merged
JCQuintas merged 13 commits into
mui:masterfrom
JCQuintas:fix/datagrid-getcolumn-nullable
Jul 23, 2026
Merged

[data grid] Fix getColumn return type not reflecting that it can return undefined#23165
JCQuintas merged 13 commits into
mui:masterfrom
JCQuintas:fix/datagrid-getcolumn-nullable

Conversation

@JCQuintas

Copy link
Copy Markdown
Member

Fixes #23143

Summary

gridColumnApi.getColumn is implemented as a bare lookup (gridColumnLookupSelector(apiRef)[field]) and returns undefined for any field absent from the lookup — a reachable state during a column set transition — but its declared type promises a non-nullable GridStateColDef. Downstream TypeScript consumers dereferencing the result as the type invites crash with a TypeError at runtime, and the type can't be corrected downstream (return types are covariant in the api interface constraints).

  • Type getColumn as (field: string) => GridStateColDef | undefined to match the implementation, and update the JSDoc. This matches getRow/getRowNode, which are already typed nullable.
  • Guard the internal call sites surfaced by the type change: optional chaining where undefined falls through safely, early return where the operation is meaningless without the column (sorting, reordering, resizing, editing a removed column — these paths previously threw TypeError).
  • setColumnIndex with an unknown field previously moved the last column via splice(-1, 1); it is now a no-op.
  • Widen getRowValue/getRowFormattedValue/getCellParamsForRow colDef parameter types to | undefined — the implementations already guard it. getCellParams/getColumnHeaderParams runtime behavior is unchanged (params may carry an undefined colDef during a transition, as before; the public params types are untouched).

Note: the getColumn return type change is compile-time breaking for strict TypeScript consumers that dereference the result without a guard — flagging in case it should wait for a major or get a changelog callout.

@JCQuintas JCQuintas added type: bug It doesn't behave as expected. scope: data grid Changes related to the data grid. plan: Pro Impact at least one Pro user. plan: Premium Impact at least one Premium user. labels Jul 17, 2026
@JCQuintas JCQuintas self-assigned this Jul 17, 2026
@code-infra-dashboard

code-infra-dashboard Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy preview

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 🔺+84B(+0.02%) 🔺+16B(+0.01%)
@mui/x-data-grid-pro 🔺+116B(+0.02%) 🔺+23B(+0.01%)
@mui/x-data-grid-premium 🔺+132B(+0.02%) 🔺+37B(+0.02%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-scheduler 0B(0.00%) 0B(0.00%)
@mui/x-scheduler-premium 0B(0.00%) 0B(0.00%)
@mui/x-chat 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

Comment on lines +26 to +28
if (!column) {
return null;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Keep in mind this is "breaking change" as it changes the typescript. It corrects it though. In a few places internally we are already checking for presence, possibly our users are too.

IDK what is the pattern we follow in these cases though. On charts we generally used to accept it as a bug, and this fixes the bug.

@JCQuintas JCQuintas added breaking change Introduces changes that are not backward compatible. and removed plan: Pro Impact at least one Pro user. plan: Premium Impact at least one Premium user. labels Jul 17, 2026
@JCQuintas
JCQuintas requested review from arminmeh and michelengelen and removed request for arminmeh July 20, 2026 15:37
@JCQuintas
JCQuintas marked this pull request as ready for review July 20, 2026 15:37
* @returns {{GridStateColDef | undefined}} The [[GridStateColDef]], or `undefined` if the field is not in the current column set.
*/
getColumn: (field: string) => GridStateColDef;
getColumn: (field: string) => GridStateColDef | undefined;

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.

This is fine, because field is a string, which means that you can always ask for something you didn't define.
We should not update other API and helpers that need GridColDef (getRowValue, getRowFormattedValue, ...) to accept undefined as well, just because this type changed.
Those don't make sense if there is no column, and the guards should be placed in the code before those are called to make sure that we have a column definition.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If I understand you correctly, having GridColDef | undefined doesn't make sense, but getColumn is ok to return undefined?

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 meant as an input in other places.
If I need a formatted value of the column, then the parameter should be GridColDef, not GridColDef | undefined
This particular line is fine. My comment was for the changes related to this change

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've updated the code, let me know if there any missing pieces 😆

Revert widening of getRowValue/getRowFormattedValue/getCellParamsForRow
and getAvailableAggregationFunctions to accept an undefined colDef.
Those APIs are meaningless without a column definition, so callers guard
instead. getCellParams/getColumnHeaderParams keep their previous runtime
behavior (see mui#22831) via a cast at the params boundary.

Copilot AI left a comment

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.

Pull request overview

This PR updates the Data Grid column API typing so gridColumnApi.getColumn accurately reflects runtime behavior by allowing undefined when a field is not present during column-set transitions, and then hardens internal call sites and docs accordingly.

Changes:

  • Update GridColumnApi.getColumn return type and JSDoc to GridStateColDef | undefined.
  • Add guards / optional chaining across sorting, filtering, editing, column resize/reorder, and premium features to avoid TypeError during transitions.
  • Update generated API docs JSON and docs demo code paths that call getColumn.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/x-data-grid/src/models/api/gridColumnApi.ts Widen getColumn return type and update documentation.
packages/x-data-grid/src/hooks/features/sorting/useGridSorting.ts Early-return when getColumn returns undefined.
packages/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts Adjust params construction to compile with getColumn possibly returning undefined.
packages/x-data-grid/src/hooks/features/filter/useGridFilter.tsx Avoid dereferencing filterOperators when the column is missing.
packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts Add null-guards for column-dependent editing logic.
packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts Add null-guards for column-dependent editing logic.
packages/x-data-grid/src/hooks/features/columns/useGridColumns.tsx Make setColumnIndex a no-op when the field is unknown.
packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx Bail out of resize start when column definition is missing.
packages/x-data-grid/src/components/cell/GridEditLongTextCell.tsx Guard valueParser access when getColumn returns undefined.
packages/x-data-grid/src/components/cell/GridEditInputCell.tsx Guard valueParser access when getColumn returns undefined.
packages/x-data-grid-pro/src/hooks/features/columnReorder/useGridColumnReorder.tsx Guard reorder event/logic when dragged/target column is missing.
packages/x-data-grid-premium/src/hooks/features/chartsIntegration/useGridChartsIntegration.tsx Avoid spreading a potentially missing grouped column definition.
packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts Guard actions-column checks when column is missing.
packages/x-data-grid-premium/src/components/GridGroupingCriteriaCell.tsx Guard renderCell access when grouped column definition is missing.
packages/x-data-grid-premium/src/components/chartsPanel/data/GridChartsPanelDataField.tsx Avoid calling aggregation discovery with an undefined column.
docs/pages/x/api/data-grid/grid-api.json Update API docs to reflect getColumn returning undefined.
docs/data/data-grid/components/filter-panel/FilterPanelTriggerDescription.tsx Guard docs demo logic when getColumn returns undefined.
docs/data/data-grid/components/filter-panel/FilterPanelTriggerDescription.js Generated JS counterpart updated to match the TSX demo behavior.

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

* Returns the [[GridColDef]] for the given `field`.
* @param {string} field The column field.
* @returns {{GridStateColDef}} The [[GridStateColDef]].
* @returns {{GridStateColDef | undefined}} The [[GridStateColDef]], or `undefined` if the field is not in the current column set.
Comment on lines 35 to 39
const getColumnHeaderParams = React.useCallback<GridParamsApi['getColumnHeaderParams']>(
(field) => ({
field,
colDef: apiRef.current.getColumn(field),
colDef: apiRef.current.getColumn(field) as GridStateColDef,
}),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@arminmeh I think we have two approaches we can do here:

  1. Make GridCellParams['colDef'] allow undefined
  2. Make the functions getCellParams/getColumnHeaderParams throw MissingColumnError like the getRowParams. But this changes behaviour, and instead of users checking for undefined they would need to use a try/catch 😬

Technically throwing has precedence in the getRowParams, so technically it makes sense, but the side effects for the user might be too big for a "bugfix"

Do you have any preference?

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.

Well, they allowed undefined before, just not explicitly, so we should keep it that way. Throwing an error would have a much larger impact. We can work on the alignment in the next major.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should be good, also updated some demos that needed it with the change

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 didn't realize that the impact was so big.
Now we have a situation where renderEditCell params send colDef as undefined, which also doesn't make sense, because it is an edit renderer for this column. I can't be in the renderer if the column doesn't exist 😀

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 have looked at it as well. Seems that, for now, the best option is to case (like it was before the big change)

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.

Reverted with bd21e92 (this PR)

Comment on lines 117 to 122
return apiRef.current.getCellParamsForRow<any, any, any, any>(id, field, row, {
colDef:
props.listView && props.listViewColumn?.field === field
? gridListColumnSelector(apiRef)!
: apiRef.current.getColumn(field),
// Params keep a non-nullable `colDef`, but it can be `undefined` at runtime for an unknown field.
colDef: (props.listView && props.listViewColumn?.field === field
? gridListColumnSelector(apiRef)!
: apiRef.current.getColumn(field)) as GridStateColDef,
rowNode,
arminmeh
arminmeh previously approved these changes Jul 22, 2026

@arminmeh arminmeh left a comment

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.

LGTM, appart from the grid params API changes. I have the same questions as Copilot.
Do we allow undefined for those params? If so, we should align their return type.

dataFieldName: column.field,
depth: rowGroupingModel.indexOf(column.field),
};
if (columnDefinition) {

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.

Note:
We shouldn't be able to reach this line with columnDefinition === undefined, since there is isColumnGrouped check above. But we can keep the check to avoid casting.

Comment on lines 344 to 345

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.

now that we are here, this condition is unnecessary.
At line 345, isColumnGrouped is truthy, which means that we can just assign getRowGroupingFieldFromGroupingCriteria result to it.

arminmeh and others added 6 commits July 22, 2026 09:08
getCellParams/getColumnHeaderParams return params for any field string, so
their colDef is undefined when the field is not in the current column set.
Type it as such instead of casting, and guard the consumers that dereference
it.
@arminmeh
arminmeh dismissed their stale review July 22, 2026 18:11

Type change made a bigger impact

@JCQuintas JCQuintas changed the title [data grid] Fix getColumn return type not reflecting that it can return undefined [data grid] Fix getColumn return type not reflecting that it can return undefined Jul 23, 2026
@JCQuintas
JCQuintas enabled auto-merge (squash) July 23, 2026 09:30
@JCQuintas
JCQuintas merged commit 643702e into mui:master Jul 23, 2026
23 checks passed
@JCQuintas
JCQuintas deleted the fix/datagrid-getcolumn-nullable branch July 23, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Introduces changes that are not backward compatible. scope: data grid Changes related to the data grid. type: bug It doesn't behave as expected. typescript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[data grid] gridColumnApi.getColumn is typed non-nullable but returns undefined for fields absent from the lookup

3 participants