fix(sim-control): narrow SimControlParseResult via type predicates

3fe0ea5 left `nuxt typecheck` failing on main:

  useLiveAtcSession.ts: Property 'reason' does not exist on type
  'SimControlParseResult'.

Cause: the union is discriminated by a boolean, and the project builds
with `strict: false` (nuxt.config.ts). With strictNullChecks off,
TypeScript does not treat `true`/`false` literal types as discriminants,
so `if (r.matched) … else r.reason` never narrows. Nothing about the
sim-control types themselves is wrong — the same three lines with any
boolean-discriminated union fail identically.

Adds isSimControlMatch/isSimControlRejection type predicates, which narrow
regardless of strictNullChecks, and routes the one caller through them.
Turning on strictNullChecks would fix it at the root but is a repo-wide
change, not a bug fix.

The pre-push hook was correctly refusing to push this; origin/main is
clean, so the breakage never escaped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
itsrubberduck
2026-07-16 17:51:09 +02:00
parent 7d49f18cc6
commit 672ac18ac7
2 changed files with 27 additions and 5 deletions

View File

@@ -8,6 +8,8 @@ import { useApi } from '~/composables/useApi'
import type { useRadioSpeech } from '~/composables/useRadioSpeech'
import useCommunicationsEngine from '../../shared/utils/communicationsEngine'
import {
isSimControlMatch,
isSimControlRejection,
parseSimControl,
simControlRejectionSpeech,
simControlResultSpeech,
@@ -331,11 +333,14 @@ export function useLiveAtcSession(
// anchors already keep it from ever matching real ICAO phraseology either way.
if (bridgeConnected.value) {
const simResult = parseSimControl(transcript)
if (simResult.matched) {
if (isSimControlMatch(simResult)) {
void sendSimControlCommand(simResult.command)
return
}
if (simResult.reason !== 'no_intent') {
// A rejection with a reason means the pilot clearly addressed their sim but
// got something wrong — tell them. 'no_intent' just means this was ordinary
// radio traffic, so it falls through to the flow untouched.
if (isSimControlRejection(simResult) && simResult.reason !== 'no_intent') {
const reply = simControlRejectionSpeech(simResult.reason)
scheduleControllerSpeech(reply)
appendLogEntry('atc', reply, currentState.value?.id ?? '', { frequency: frequencies.value.active })