Repository: angular/angular
File: packages/router/src/directives/router_link_active.ts (TypeScript)

Code before:
      });
      if (hasActiveLinks && this.ariaCurrentWhenActive !== undefined) {
        this.renderer.setAttribute(
          this.element.nativeElement,
          'aria-current',
          this.ariaCurrentWhenActive.toString(),
        );
      } else {
        this.renderer.removeAttribute(this.element.nativeElement, 'aria-current');
      }

      // Only emit change if the active state changed.
      if (this._isActive !== hasActiveLinks) {
        this._isActive = hasActiveLinks;
        this.cdr.markForCheck();
        // Emit on isActiveChange after classes are updated
        this.isActiveChange.emit(hasActiveLinks);
      }
    });
  }

  private isLinkActive(router: Router): (link: RouterLink) => boolean {
    const options: IsActiveMatchOptions = isActiveMatchOptions(this.routerLinkActiveOptions)
      ? this.routerLinkActiveOptions
      : // While the types should disallow `undefined` here, it's possible without strict inputs
        this.routerLinkActiveOptions.exact || false
        ? {...exactMatchOptions}
        : {...subsetMatchOptions};

    return (link: RouterLink) => {
      const urlTree = link.urlTree;
      return urlTree ? untracked(isActive(urlTree, router, options)) : false;
    };
  }

  private hasActiveLinks(): boolean {
    const isActiveCheckFn = this.isLinkActive(this.router);
    return (this.link && isActiveCheckFn(this.link)) || this.links.some(isActiveCheckFn);
  }
}

/**
 * Use instead of `'paths' in options` to be compatible with property renaming
 */
function isActiveMatchOptions(
  options: {exact: boolean} | IsActiveMatchOptions,
): options is IsActiveMatchOptions {
  return !!(options as IsActiveMatchOptions).paths;
}


Review comment (suggestion):
nit: While we're editing this line, we should use the right operator
```suggestion
        this.routerLinkActiveOptions.exact ?? false
```

Code after:
      });
      if (hasActiveLinks && this.ariaCurrentWhenActive !== undefined) {
        this.renderer.setAttribute(
          this.element.nativeElement,
          'aria-current',
          this.ariaCurrentWhenActive.toString(),
        );
      } else {
        this.renderer.removeAttribute(this.element.nativeElement, 'aria-current');
      }

      // Only emit change if the active state changed.
      if (this._isActive !== hasActiveLinks) {
        this._isActive = hasActiveLinks;
        this.cdr.markForCheck();
        // Emit on isActiveChange after classes are updated
        this.isActiveChange.emit(hasActiveLinks);
      }
    });
  }

  private isLinkActive(router: Router): (link: RouterLink) => boolean {
    const options: IsActiveMatchOptions = isActiveMatchOptions(this.routerLinkActiveOptions)
      ? this.routerLinkActiveOptions
      : // While the types should disallow `undefined` here, it's possible without strict inputs
        (this.routerLinkActiveOptions.exact ?? false)
        ? {...exactMatchOptions}
        : {...subsetMatchOptions};

    return (link: RouterLink) => {
      const urlTree = link.urlTree;
      return urlTree ? untracked(isActive(urlTree, router, options)) : false;
    };
  }

  private hasActiveLinks(): boolean {
    const isActiveCheckFn = this.isLinkActive(this.router);
    return (this.link && isActiveCheckFn(this.link)) || this.links.some(isActiveCheckFn);
  }
}

/**
 * Use instead of `'paths' in options` to be compatible with property renaming
 */
function isActiveMatchOptions(
  options: {exact: boolean} | IsActiveMatchOptions,
): options is IsActiveMatchOptions {
  return !!(options as IsActiveMatchOptions).paths;
}