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

  1. Ensure the bound expression is an actual function reference: `[compareWith]="(o1, o2) => o1.id === o2.id"` or a defined arrow/method property
  2. Fix typos/undefined bindings so the property is defined before the select initializes
  3. 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

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


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/a63c9be3c0512dc1. Report an issue: GitHub.