Allow non-null assertions in tests; refactor two production hotspots to drop them

- eslint.config.js: disable no-non-null-assertion for *.test.ts/tsx and
  tests/** (fixture-driven tests routinely use arr[0]! after a length
  check — signal there is low).
- closestFlight.ts: replace flights.legs[0]! / flights[flights.length-1]!
  with explicit null checks.
- FlightDetailsAccordion.tsx: refactor transition + meal/service
  branches to use local consts narrowed by a preceding truthy check,
  dropping the `leg.transition!.registration!` patterns.

Warning count: 190 → 65. Remaining warnings are pre-existing production-code
non-null assertions spread across the codebase.
This commit is contained in:
2026-04-20 08:24:01 +03:00
parent a982d9a669
commit 5c47498472
3 changed files with 46 additions and 23 deletions
+12
View File
@@ -177,4 +177,16 @@ export default [
],
},
},
// Test files get a looser ruleset: non-null assertions are idiomatic
// when fixtures guarantee presence (`arr[0]!` after an explicit length
// check). Production code keeps the warning; tests don't.
{
files: [
"src/**/*.test.{ts,tsx}",
"tests/**/*.{ts,tsx}",
],
rules: {
"@typescript-eslint/no-non-null-assertion": "off",
},
},
];
+16 -7
View File
@@ -11,13 +11,17 @@
import type { ISimpleFlight, IFlightLeg } from "./types.js";
function firstLeg(flight: ISimpleFlight): IFlightLeg {
return flight.routeType === "Direct" ? flight.leg : flight.legs[0]!;
if (flight.routeType === "Direct") return flight.leg;
const first = flight.legs[0];
if (!first) throw new Error("MultiLeg flight has no legs");
return first;
}
function lastLeg(flight: ISimpleFlight): IFlightLeg {
return flight.routeType === "Direct"
? flight.leg
: flight.legs[flight.legs.length - 1]!;
if (flight.routeType === "Direct") return flight.leg;
const last = flight.legs[flight.legs.length - 1];
if (!last) throw new Error("MultiLeg flight has no legs");
return last;
}
function pickTime(flight: ISimpleFlight, isArrival: boolean): string | null {
@@ -78,9 +82,14 @@ export function findClosestFlightId(
if (!isTodayYyyymmdd(opts.searchDate, now)) {
// For future days, show the first flight; for past days, the last.
const firstMs = parseTimeMs(pickTime(flights[0]!, opts.isArrival));
if (firstMs !== null && nowMs < firstMs) return flights[0]!.id;
return flights[flights.length - 1]!.id;
// We've already short-circuited on `!flights.length` above so both
// ends are defined.
const first = flights[0];
const last = flights[flights.length - 1];
if (!first || !last) return null;
const firstMs = parseTimeMs(pickTime(first, opts.isArrival));
if (firstMs !== null && nowMs < firstMs) return first.id;
return last.id;
}
let best: string | null = flights[0]?.id ?? null;
@@ -140,38 +140,38 @@ export const FlightDetailsAccordion: FC<FlightDetailsAccordionProps> = ({ leg, v
const rows: RowDef[] = [];
if (shouldShowTransition(leg.transition?.registration, leg.status, viewType)) {
const item = leg.transition!.registration!;
const registration = leg.transition?.registration;
if (registration && shouldShowTransition(registration, leg.status, viewType)) {
rows.push({
id: "registration",
icon: ICON_REGISTRATION,
title: t("DETAILS.REGISTRATION"),
statusStatus: item.status,
body: <TransitionTimes item={item} testId="registration-times" />,
statusStatus: registration.status,
body: <TransitionTimes item={registration} testId="registration-times" />,
legacyTestId: "registration-panel",
isTransition: true,
});
}
if (shouldShowTransition(leg.transition?.boarding, leg.status, viewType)) {
const item = leg.transition!.boarding!;
const boarding = leg.transition?.boarding;
if (boarding && shouldShowTransition(boarding, leg.status, viewType)) {
rows.push({
id: "boarding",
icon: ICON_BOARDING,
title: t("DETAILS.BOARDING"),
statusStatus: item.status,
body: <TransitionTimes item={item} testId="boarding-times" />,
statusStatus: boarding.status,
body: <TransitionTimes item={boarding} testId="boarding-times" />,
legacyTestId: "boarding-panel",
isTransition: true,
});
}
if (shouldShowTransition(leg.transition?.deboarding, leg.status, viewType)) {
const item = leg.transition!.deboarding!;
const deboarding = leg.transition?.deboarding;
if (deboarding && shouldShowTransition(deboarding, leg.status, viewType)) {
rows.push({
id: "deboarding",
icon: ICON_DEBOARDING,
title: t("DETAILS.DEBOARDING"),
statusStatus: item.status,
body: <TransitionTimes item={item} testId="deboarding-times" />,
statusStatus: deboarding.status,
body: <TransitionTimes item={deboarding} testId="deboarding-times" />,
legacyTestId: "deboarding-panel",
isTransition: true,
});
@@ -187,20 +187,22 @@ export const FlightDetailsAccordion: FC<FlightDetailsAccordionProps> = ({ leg, v
body: <AircraftPanel equipment={leg.equipment} />,
});
}
if ((leg.equipment.meal?.length ?? 0) > 0) {
const meals = leg.equipment.meal;
if (meals && meals.length > 0) {
rows.push({
id: "meal",
icon: ICON_MEAL,
title: t("SHARED.FOOD"),
body: <MealPanel meals={leg.equipment.meal!} />,
body: <MealPanel meals={meals} />,
});
}
if ((leg.equipment.aircraft?.actual?.onBoardServices?.length ?? 0) > 0) {
const onBoardServices = leg.equipment.aircraft?.actual?.onBoardServices;
if (onBoardServices && onBoardServices.length > 0) {
rows.push({
id: "services",
icon: ICON_SERVICES,
title: t("DETAILS.ON_BOARD_SERVICES"),
body: <ServicesPanel services={leg.equipment.aircraft!.actual!.onBoardServices!} />,
body: <ServicesPanel services={onBoardServices} />,
});
}