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.jsonusesstrict: falseandstrictTemplates: 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 undertests/frontend/. All compile vianpm 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
-
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.
- Add
-
Refactor
submit()insetup-create-admin.component.ts:- Replace the
if (result.ok) { ... return; } ... if (result.code === ...) ...sequence with anif (result.ok) { ... return; } const failure = result;pattern. Becauseresult.okistrue | 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
CreateAdminFailurefrom./setup-create-admin.service. - The cast
const failure: CreateAdminFailure = resultafter thereturnis 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; theas-cast is one token shorter and more idiomatic. Choose:const failure = result as CreateAdminFailure; - Replace the
-
No HTML/CSS/service file changes.
4. Test Strategy
- Target Unit Test File: no changes. The existing
tests/frontend/setup-create-admin.spec.tsalready 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:
npm --workspace frontend run buildmust complete withoutTS2339errors.npm testmust 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,CreateAdminFailureshapes remain as defined. - No docs change required — implementation detail only.
6. Execution Checklist
- Edit
setup-create-admin.component.ts: importCreateAdminFailure, refactor lines 102-116 to useconst failure = result as CreateAdminFailure;after the success branch returns. - Run
npm --workspace frontend run buildand confirm 0 errors. - Run
npm testand confirmTest Suites: 20 passed, 20 total.