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:
@@ -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",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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} />,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user