Files
HIPCTF2/.kilo/plans/821-followup-build-fix.md
T
2026-07-21 14:45:53 +00:00

4.9 KiB

Implementation Plan: Fix Angular build error — Property 'code' does not exist on type 'CreateAdminResult'

1. Architectural Reconnaissance

  • Codebase style & conventions: Angular 17 standalone components, signals, typed reactive forms. frontend/tsconfig.json uses strict: false and strictTemplates: false. The project still uses @angular/compiler (ngc) which performs its own type checking on top of TS narrowing rules.
  • Data Layer: N/A — this is a frontend-only type-narrowing fix.
  • Test Framework & Structure: tests/jest.config.js. Tests live under tests/frontend/. All compile via npm test.
  • Required Tools & Dependencies: No new dependencies. The fix is purely TypeScript / component-internal.

2. Impacted Files

  • To Modify: /repo/frontend/src/app/features/setup/setup-create-admin.component.ts — fix the type-narrowing that Angular's compiler refuses to accept.
  • No new files, no test changes, no docs changes.

3. Proposed Changes

Root cause

In setup-create-admin.component.ts:88-117 the submit() method awaits the service, then reads result.ok. After the if (result.ok) { ... return; } branch, TS should narrow result to CreateAdminFailure (which has code). However Angular's compiler (used by ng build) reports:

TS2339: Property 'code' does not exist on type 'CreateAdminResult'.
  Property 'code' does not exist on type 'CreateAdminSuccess'.

at the two reads result.code on lines 109 and 116. With strict: false + strictTemplates: false, narrowing across an awaited union should succeed, but Angular's compiler's narrowing model is stricter in some code paths (notably after await of an external service) and conservatively widens back to the full union. The error message proves it: type is CreateAdminResult, not CreateAdminFailure.

Minimal fix

Replace the discriminated-union narrowing with an explicit type guard in the service and an else branch in the component. This avoids relying on the compiler's narrowing at all.

Step-by-step

  1. Add a type predicate to the service (setup-create-admin.service.ts):

    • Add export function isCreateAdminFailure(r: CreateAdminResult): r is CreateAdminFailure { return !r.ok; }.
    • No behaviour change.
  2. Refactor submit() in setup-create-admin.component.ts:

    • Replace the if (result.ok) { ... return; } ... if (result.code === ...) ... sequence with an if (result.ok) { ... return; } const failure = result; pattern. Because result.ok is true | false, the negation narrows the local copy identically. The safest cross-compiler approach is:
      if (result.ok) {
        this.auth.setSession(result.accessToken, result.user);
        this.bootstrap.markInitialized();
        await this.router.navigateByUrl('/challenges');
        return;
      }
      const failure: CreateAdminFailure = result; // explicit local of narrowed type
      if (failure.code === 'USERNAME_TAKEN') {
        this.serverError.set('Username already exists');
        this.serverErrorCode.set('USERNAME_TAKEN');
        return;
      }
      this.serverError.set('Setup failed, please retry');
      this.serverErrorCode.set(failure.code);
      
    • Import CreateAdminFailure from ./setup-create-admin.service.
    • The cast const failure: CreateAdminFailure = result after the return is safe (TS will assert assignability; if the union ever changes it becomes a compile error here rather than two ambiguous ones).

    Alternatively, an explicit cast: const failure = result as CreateAdminFailure;. Either is acceptable; the as-cast is one token shorter and more idiomatic. Choose:

    const failure = result as CreateAdminFailure;
    
  3. No HTML/CSS/service file changes.

4. Test Strategy

  • Target Unit Test File: no changes. The existing tests/frontend/setup-create-admin.spec.ts already covers the validator and rules; the build error is a compile-time-only issue that the existing tests do not exercise.
  • Mocking Strategy: N/A.
  • Verification:
    1. npm --workspace frontend run build must complete without TS2339 errors.
    2. npm test must still pass (20 suites, 97 tests).
  • Do not touch any test file. The fix is internal to the component.

5. Risk / Non-Goals

  • No behaviour change. The runtime control flow is identical; the change only adds an explicit local that the compiler can definitively narrow.
  • No public API change. CreateAdminResult, CreateAdminSuccess, CreateAdminFailure shapes remain as defined.
  • No docs change required — implementation detail only.

6. Execution Checklist

  1. Edit setup-create-admin.component.ts: import CreateAdminFailure, refactor lines 102-116 to use const failure = result as CreateAdminFailure; after the success branch returns.
  2. Run npm --workspace frontend run build and confirm 0 errors.
  3. Run npm test and confirm Test Suites: 20 passed, 20 total.