{"record":{"id":"a63c9be3c0512dc1","repo":"angular/components","slug":"comparewith-must-be-a-function","errorCode":null,"errorMessage":"`compareWith` must be a function.","messagePattern":"`compareWith` must be a function\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/material/select/select.ts","lineNumber":469,"sourceCode":"  }\n  private _multiple: boolean = false;\n\n  /** Whether to center the active option over the trigger. */\n  @Input({transform: booleanAttribute})\n  disableOptionCentering = this._defaultOptions?.disableOptionCentering ?? false;\n\n  /**\n   * Function to compare the option values with the selected values. The first argument\n   * is a value from an option. The second is a value from the selection. A boolean\n   * should be returned.\n   */\n  @Input()\n  get compareWith() {\n    return this._compareWith;\n  }\n  set compareWith(fn: (o1: any, o2: any) => boolean) {\n    if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n      throw getMatSelectNonFunctionValueError();\n    }\n    this._compareWith = fn;\n    if (this._selectionModel) {\n      // A different comparator means the selection could change.\n      this._initializeSelection();\n    }\n  }\n\n  /** Value of the select control. */\n  @Input()\n  get value(): any {\n    return this._value;\n  }\n  set value(newValue: any) {\n    const hasAssigned = this._assignValue(newValue);\n\n    if (hasAssigned) {\n      this._onChange(newValue);","sourceCodeStart":451,"sourceCodeEnd":487,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/material/select/select.ts#L451-L487","documentation":"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.","triggerScenarios":"`[compareWith]=\"compareFn\"` where `compareFn` is undefined at bind time, or `[compareWith]=\"'compareFn'\"` (string), or a component property assigned before it becomes a function.","commonSituations":"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.","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"],"exampleFix":"// before\n[compareForm]=\"'compareById'\"\n// after\n[compareWith]=\"compareById\"  // compareById(o1: T, o2: T) { return o1.id === o2.id; }","handlingStrategy":"type-guard","validationCode":"function isCompareFn(fn: unknown): fn is (o1: any, o2: any) => boolean {\n  return typeof fn === 'function';\n}\nif (!isCompareFn(component.compareById)) {\n  throw new Error('compareWith must be a function reference');\n}","typeGuard":"function isCompareFn(fn: unknown): fn is (o1: unknown, o2: unknown) => boolean {\n  return typeof fn === 'function';\n}","tryCatchPattern":"try {\n  fixture.detectChanges(); // throws during mat-select init if compareWith invalid\n} catch (e) {\n  if (String(e?.message).includes('must be a function')) {\n    fail('[compareWith] must bind a function reference, not a string or undefined value');\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["angular","material","select","input","type-error"],"backgroundTag":"invalid-input-type","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}