angular/components · error
`compareWith` must be a function.
Error message
`compareWith` must be a function.
What it means
MatSelect's `compareWith` input must be a function used to compare option values for selection. Passing anything else (string name of a function, undefined via binding typo) throws this dev-mode error.
Source
Thrown at src/material/select/select.ts:469
}
private _multiple: boolean = false;
/** Whether to center the active option over the trigger. */
@Input({transform: booleanAttribute})
disableOptionCentering = this._defaultOptions?.disableOptionCentering ?? false;
/**
* Function to compare the option values with the selected values. The first argument
* is a value from an option. The second is a value from the selection. A boolean
* should be returned.
*/
@Input()
get compareWith() {
return this._compareWith;
}
set compareWith(fn: (o1: any, o2: any) => boolean) {
if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw getMatSelectNonFunctionValueError();
}
this._compareWith = fn;
if (this._selectionModel) {
// A different comparator means the selection could change.
this._initializeSelection();
}
}
/** Value of the select control. */
@Input()
get value(): any {
return this._value;
}
set value(newValue: any) {
const hasAssigned = this._assignValue(newValue);
if (hasAssigned) {
this._onChange(newValue);View on GitHub (pinned to 0411926e7d)
Solutions
- Ensure the bound expression is an actual function reference: `[compareWith]="(o1, o2) => o1.id === o2.id"` or a defined arrow/method property
- Fix typos/undefined bindings so the property is defined before the select initializes
- Do not quote the function name in the template
Example fix
// before
[compareForm]="'compareById'"
// after
[compareWith]="compareById" // compareById(o1: T, o2: T) { return o1.id === o2.id; } Defensive patterns
Strategy: type-guard
Validate before calling
function isCompareFn(fn: unknown): fn is (o1: any, o2: any) => boolean {
return typeof fn === 'function';
}
if (!isCompareFn(component.compareById)) {
throw new Error('compareWith must be a function reference');
} Type guard
function isCompareFn(fn: unknown): fn is (o1: unknown, o2: unknown) => boolean {
return typeof fn === 'function';
} Try / catch
try {
fixture.detectChanges(); // throws during mat-select init if compareWith invalid
} catch (e) {
if (String(e?.message).includes('must be a function')) {
fail('[compareWith] must bind a function reference, not a string or undefined value');
}
throw e;
} Prevention
- Bind function references, never quoted names: [compareWith]="compareById"
- Add compile-time typing: compareById: (a: T, b: T) => boolean
- Initialize comparator properties before the template renders
When it happens
Trigger: `[compareWith]="compareFn"` where `compareFn` is undefined at bind time, or `[compareWith]="'compareFn'"` (string), or a component property assigned before it becomes a function.
Common situations: Passing the function name as a string like ng-model era AngularJS; binding a method that is undefined due to lifecycle/`this` issues; template typo causing the expression to evaluate to null.
Related errors
- Cannot change `multiple` mode of select after initialization
- Unable to retrieve options for autocomplete. Autocomplete pa
- Unable to retrieve option groups for autocomplete. Autocompl
- Unsupported MatButton appearance "${appearance}"
- Cannot begin editing a chip that is not editable.
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/a63c9be3c0512dc1.
Report an issue: GitHub.