fix: narrow parseBrpAddress's return type instead of leaking the DTO

BrpAddressDto's fields are generated as optional, so returning it
directly from parseBrpAddress lost the narrowing the runtime check
already did. This broke the build once registratie-lookup.store.ts
assigned the parsed address into a stricter local type. Map to a
proper BrpAddress domain shape at the trust boundary instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-04 15:17:06 +02:00
co-authored by Claude Sonnet 5
parent 5977efe044
commit c7aed8d308
2 changed files with 22 additions and 13 deletions
@@ -41,7 +41,7 @@ export class RegistratieLookupStore {
const json = this.adresRes.value(); const json = this.adresRes.value();
if (json === undefined) return null; if (json === undefined) return null;
const parsed = parseBrpAddress(json); const parsed = parseBrpAddress(json);
return parsed.ok && parsed.value.gevonden && parsed.value.adres ? parsed.value.adres : null; return parsed.ok && parsed.value.adres ? parsed.value.adres : null;
}, },
); );
@@ -3,6 +3,13 @@ import { Result, ok, err } from '@shared/kernel/fp';
import { BrpAddressDto } from '@shared/infrastructure/api-client'; import { BrpAddressDto } from '@shared/infrastructure/api-client';
import { ApiClient } from '@shared/infrastructure/api-client'; import { ApiClient } from '@shared/infrastructure/api-client';
/** BRP address lookup, narrowed from the generated (all-optional) `BrpAddressDto`
to what `gevonden` actually guarantees. */
export interface BrpAddress {
gevonden: boolean;
adres?: { straat: string; postcode: string; woonplaats: string };
}
/** /**
* Infrastructure adapter for the BRP address lookup, reached only through our own * Infrastructure adapter for the BRP address lookup, reached only through our own
* ("BFF-lite") endpoint — the anti-corruption boundary. Data comes from the .NET * ("BFF-lite") endpoint — the anti-corruption boundary. Data comes from the .NET
@@ -21,11 +28,11 @@ export class BrpAdapter {
/** Trust-boundary parse: validate the untrusted response shape. "Geen adres" is a /** Trust-boundary parse: validate the untrusted response shape. "Geen adres" is a
valid outcome (gevonden: false), not a malformed response. ponytail: hand-written; valid outcome (gevonden: false), not a malformed response. ponytail: hand-written;
reach for a schema lib once the contract count grows. */ reach for a schema lib once the contract count grows. */
export function parseBrpAddress(json: unknown): Result<string, BrpAddressDto> { export function parseBrpAddress(json: unknown): Result<string, BrpAddress> {
if (typeof json !== 'object' || json === null) return err('brp-address: not an object'); if (typeof json !== 'object' || json === null) return err('brp-address: not an object');
const dto = json as Partial<BrpAddressDto>; const dto = json as Partial<BrpAddressDto>;
if (typeof dto.gevonden !== 'boolean') return err('brp-address: missing/invalid gevonden'); if (typeof dto.gevonden !== 'boolean') return err('brp-address: missing/invalid gevonden');
if (dto.gevonden) { if (!dto.gevonden) return ok({ gevonden: false });
const a = dto.adres; const a = dto.adres;
if ( if (
!a || !a ||
@@ -35,6 +42,8 @@ export function parseBrpAddress(json: unknown): Result<string, BrpAddressDto> {
) { ) {
return err('brp-address: missing/invalid adres'); return err('brp-address: missing/invalid adres');
} }
} return ok({
return ok({ gevonden: dto.gevonden, adres: dto.adres }); gevonden: true,
adres: { straat: a.straat, postcode: a.postcode, woonplaats: a.woonplaats },
});
} }