Skip to content

Commit a6133e8

Browse files
committed
Fix KeyHelpers.NonNullableKeys typings
* Changed all RequiredKeys, OptionalKeys and NonNullableKeys typings to omit the requested keys 1st. No effect on the type, but makes logically more sense to drop the fields (mark then with type never) and then override those fields, than to 1st make the changes and then merger them with never types. * NnnNullableKeys did not do what it's docstring said it would. Previously any optional fields were passed trough as optionals, essentially typing the field as having `undefined` as one of the field value types. Now the optional marker is removed and the value in question has to truly be a nonnullable value. `value !== null && value !== undefined` * As a result of fixing the NonNullabelKeys type, some other types derived with it started breaking code. Stop UPDATE-operation related code has been fixed to allow partial updates, and to not require all of the non-null DB fields. INSERT-operations still use the proper no-nulls type.
1 parent a600f67 commit a6133e8

File tree

6 files changed

+36
-25
lines changed

6 files changed

+36
-25
lines changed

ui/src/components/forms/stop/StopForm.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
ReusableComponentsVehicleModeEnum,
1212
ServicePatternScheduledStopPoint,
1313
} from '../../../generated/graphql';
14-
import { ScheduledStopPointSetInput } from '../../../graphql';
14+
import { PartialScheduledStopPointSetInput } from '../../../graphql';
1515
import {
1616
CreateChanges,
1717
EditChanges,
@@ -111,7 +111,7 @@ function mapFormStateToInput(state: FormState) {
111111
}
112112

113113
const isDirtyMap: {
114-
readonly [key in keyof ScheduledStopPointSetInput]: ReadonlyArray<
114+
readonly [key in keyof PartialScheduledStopPointSetInput]: ReadonlyArray<
115115
keyof FormState
116116
>;
117117
} = {
@@ -126,11 +126,11 @@ const isDirtyMap: {
126126
// Only pick changed fields, needed to keep Tiamat happy when updating fields,
127127
// not in Tiamat.
128128
function pickChangedFieldsForPatch(
129-
input: ScheduledStopPointSetInput,
129+
input: PartialScheduledStopPointSetInput,
130130
dirtyFields: Partial<Readonly<FieldNamesMarkedBoolean<FormState>>>,
131-
): ScheduledStopPointSetInput {
131+
): PartialScheduledStopPointSetInput {
132132
const dirty = Object.entries(input).filter(([key]) => {
133-
const formKeys = isDirtyMap[key as keyof ScheduledStopPointSetInput];
133+
const formKeys = isDirtyMap[key as keyof PartialScheduledStopPointSetInput];
134134
if (formKeys) {
135135
return formKeys.some((formKey) => dirtyFields[formKey]);
136136
}

ui/src/components/map/stops/EditStopLayer.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import React, {
77
import { useTranslation } from 'react-i18next';
88
import { MapLayerMouseEvent } from 'react-map-gl/maplibre';
99
import { useDispatch } from 'react-redux';
10-
import { ScheduledStopPointSetInput, StopWithLocation } from '../../../graphql';
10+
import {
11+
PartialScheduledStopPointSetInput,
12+
StopWithLocation,
13+
} from '../../../graphql';
1114
import {
1215
CreateChanges,
1316
DeleteChanges,
@@ -167,7 +170,7 @@ export const EditStopLayer = forwardRef<EditStoplayerRef, Props>(
167170

168171
if (stopId) {
169172
// if this is a stop existing on the backend, also prepare the changes to be confirmed
170-
const patch: ScheduledStopPointSetInput = {
173+
const patch: PartialScheduledStopPointSetInput = {
171174
measured_location: mapLngLatToGeoJSON(event.lngLat.toArray()),
172175
};
173176
setIsLoadingBrokenRoutes(true);

ui/src/graphql/servicePattern.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ export type ScheduledStopPointSetInput = NonNullableKeys<
2323
| 'priority'
2424
>;
2525

26+
// Copy of ScheduledStopPointSetInput that can be used for update scenarios,
27+
// when it is not necessary to always update the non-nullable fields.
28+
export type PartialScheduledStopPointSetInput =
29+
Partial<ScheduledStopPointSetInput>;
30+
2631
const SCHEDULED_STOP_POINT_DEFAULT_FIELDS = gql`
2732
fragment scheduled_stop_point_default_fields on service_pattern_scheduled_stop_point {
2833
priority

ui/src/hooks/stop-registry/useEditStopBasicDetails.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import {
1414
useGetStopWithRouteGraphDataByIdLazyQuery,
1515
useUpdateStopPlaceMutation,
1616
} from '../../generated/graphql';
17-
import { ScheduledStopPointSetInput, mapStopResultToStop } from '../../graphql';
17+
import {
18+
PartialScheduledStopPointSetInput,
19+
mapStopResultToStop,
20+
} from '../../graphql';
1821
import {
1922
InternalError,
2023
TimingPlaceRequiredError,
@@ -37,7 +40,7 @@ interface EditRoutesAndLinesParams {
3740
interface EditRoutesAndLinesChanges {
3841
stopId: UUID;
3942
stopLabel: string;
40-
patch: ScheduledStopPointSetInput;
43+
patch: PartialScheduledStopPointSetInput;
4144
editedStop: ServicePatternScheduledStopPoint;
4245
deleteStopFromRoutes: RouteUniqueFieldsFragment[];
4346
deleteStopFromJourneyPatternIds?: UUID[];

ui/src/hooks/stops/useEditStop.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
useGetStopWithRouteGraphDataByIdLazyQuery,
2222
} from '../../generated/graphql';
2323
import {
24-
ScheduledStopPointSetInput,
24+
PartialScheduledStopPointSetInput,
2525
mapGetRoutesBrokenByStopChangeResult,
2626
mapStopResultToStop,
2727
} from '../../graphql';
@@ -135,13 +135,13 @@ const GQL_EDIT_STOP_PLACE = gql`
135135
interface EditParams {
136136
stopId: UUID;
137137
stopPlaceRef?: string | null;
138-
patch: ScheduledStopPointSetInput;
138+
patch: PartialScheduledStopPointSetInput;
139139
}
140140

141141
export interface EditChanges {
142142
stopId: UUID;
143143
stopLabel: string;
144-
patch: ScheduledStopPointSetInput;
144+
patch: PartialScheduledStopPointSetInput;
145145
stopPlacePatch: StopRegistryStopPlaceInput | null;
146146
editedStop: ScheduledStopPointAllFieldsFragment;
147147
deleteStopFromRoutes: RouteUniqueFieldsFragment[];
@@ -152,7 +152,7 @@ export interface EditChanges {
152152
export interface BrokenRouteCheckParams {
153153
newLink: InfrastructureNetworkInfrastructureLink;
154154
newDirection: InfrastructureNetworkDirectionEnum;
155-
newStop: ScheduledStopPointSetInput;
155+
newStop: PartialScheduledStopPointSetInput;
156156
label: string;
157157
priority: number;
158158
stopId: UUID | null;
@@ -178,7 +178,7 @@ function mapEditChangesToVariables(
178178

179179
function stopPointPatchToStopPlacePatch(
180180
stopPlaceRef: string | null | undefined,
181-
patch: ScheduledStopPointSetInput,
181+
patch: PartialScheduledStopPointSetInput,
182182
stopPlace: Pick<StopRegistryStopPlace, 'keyValues'> | null,
183183
): StopRegistryStopPlaceInput | null {
184184
if (!stopPlaceRef) {
@@ -298,7 +298,7 @@ function useGetConflictingStops() {
298298
return async (
299299
stopId: string,
300300
label: string,
301-
patch: ScheduledStopPointSetInput,
301+
patch: PartialScheduledStopPointSetInput,
302302
stopWithRouteGraphData: ServicePatternScheduledStopPoint,
303303
) => {
304304
const hasEditedValidity =
@@ -340,7 +340,7 @@ function useOnStopLocationChanged() {
340340

341341
return async (
342342
oldStop: ScheduledStopPointAllFieldsFragment,
343-
newStop: ScheduledStopPointSetInput,
343+
newStop: PartialScheduledStopPointSetInput,
344344
stopId: UUID,
345345
): Promise<OnStopLocationChangedResult> => {
346346
// if we modified the location of the stop, have to also fetch the new infra link and direction
@@ -377,7 +377,7 @@ function useGetLocationChanges() {
377377

378378
return async (
379379
stopId: string,
380-
patch: ScheduledStopPointSetInput,
380+
patch: PartialScheduledStopPointSetInput,
381381
stopWithRouteGraphData: ServicePatternScheduledStopPoint,
382382
): Promise<Partial<OnStopLocationChangedResult>> => {
383383
const newLocation = patch.measured_location;
@@ -399,7 +399,7 @@ function useValidateTimingPlaceChanges() {
399399

400400
return async (
401401
stopLabel: string,
402-
patch: ScheduledStopPointSetInput,
402+
patch: PartialScheduledStopPointSetInput,
403403
stopWithRouteGraphData: ServicePatternScheduledStopPoint,
404404
) => {
405405
if (patch.timing_place_id === undefined) {

ui/src/types/KeyHelpers.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Make given keys of a type required, leave the rest as-is
2-
export type RequiredKeys<T, K extends keyof T> = Required<Pick<T, K>> &
3-
Omit<T, K>;
2+
export type RequiredKeys<T, K extends keyof T> = Omit<T, K> &
3+
Required<Pick<T, K>>;
44

55
// Make given keys of a type optional, leave the rest as-is
6-
export type OptionalKeys<T, K extends keyof T> = Partial<Pick<T, K>> &
7-
Omit<T, K>;
6+
export type OptionalKeys<T, K extends keyof T> = Omit<T, K> &
7+
Partial<Pick<T, K>>;
88

99
// Make given keys of a type not nullable, leave the rest as-is
10-
export type NonNullableKeys<T, K extends keyof T> = {
11-
[P in K]: NonNullable<T[P]>;
12-
} & Omit<T, K>;
10+
export type NonNullableKeys<T, K extends keyof T> = Omit<T, K> & {
11+
[P in K]-?: NonNullable<T[P]>;
12+
};

0 commit comments

Comments
 (0)