Skip to content

Commit 44eea76

Browse files
authored
Fix radio widget parser types (#2378)
## Summary: The types for the migration functions were wrong; I think typechecking was still passing because `PerseusRadioWidgetOptions` is the union of all the various versions, and most of its fields are optional. Issue: none ## Test plan: `pnpm tsc` Author: benchristel Reviewers: jeremywiebe, benchristel, handeyeco, Myranae Required Reviewers: Approved By: jeremywiebe Checks: ✅ 8 checks were successful Pull Request URL: #2378
1 parent 21a5db5 commit 44eea76

File tree

4 files changed

+34
-12
lines changed

4 files changed

+34
-12
lines changed

.changeset/tidy-balloons-hammer.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@khanacademy/perseus-core": patch
3+
---
4+
5+
Fix Radio widget parser types; the parser now always sets
6+
`PerseusRadioWidgetOptions.noneOfTheAbove` to either `false` or `undefined`
7+
(whereas before it might be missing entirely).

packages/perseus-core/src/parse-perseus-json/perseus-parsers/radio-widget.ts

+9-12
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import {versionedWidgetOptions} from "./versioned-widget-options";
1515
import {parseWidgetWithVersion} from "./widget";
1616
import {parseWidgetsMap} from "./widgets-map";
1717

18-
import type {RadioWidget} from "../../data-schema";
19-
import type {ParsedValue, Parser} from "../parser-types";
18+
import type {ParsedValue} from "../parser-types";
2019

2120
const parseWidgetsMapOrUndefined = defaulted(
2221
// There is an import cycle between radio-widget.ts and
@@ -27,7 +26,7 @@ const parseWidgetsMapOrUndefined = defaulted(
2726
);
2827

2928
const version2 = optional(object({major: constant(2), minor: number}));
30-
const parseRadioWidgetV2: Parser<RadioWidget> = parseWidgetWithVersion(
29+
const parseRadioWidgetV2 = parseWidgetWithVersion(
3130
version2,
3231
constant("radio"),
3332
object({
@@ -58,7 +57,7 @@ const parseRadioWidgetV2: Parser<RadioWidget> = parseWidgetWithVersion(
5857
);
5958

6059
const version1 = optional(object({major: constant(1), minor: number}));
61-
const parseRadioWidgetV1: Parser<RadioWidget> = parseWidgetWithVersion(
60+
const parseRadioWidgetV1 = parseWidgetWithVersion(
6261
version1,
6362
constant("radio"),
6463
object({
@@ -89,7 +88,7 @@ const parseRadioWidgetV1: Parser<RadioWidget> = parseWidgetWithVersion(
8988

9089
function migrateV1ToV2(
9190
widget: ParsedValue<typeof parseRadioWidgetV1>,
92-
): RadioWidget {
91+
): ParsedValue<typeof parseRadioWidgetV2> {
9392
const {options} = widget;
9493
return {
9594
...widget,
@@ -102,7 +101,7 @@ function migrateV1ToV2(
102101
}
103102

104103
const version0 = optional(object({major: constant(0), minor: number}));
105-
const parseRadioWidgetV0: Parser<RadioWidget> = parseWidgetWithVersion(
104+
const parseRadioWidgetV0 = parseWidgetWithVersion(
106105
version0,
107106
constant("radio"),
108107
object({
@@ -132,8 +131,8 @@ const parseRadioWidgetV0: Parser<RadioWidget> = parseWidgetWithVersion(
132131
);
133132

134133
function migrateV0ToV1(
135-
widget: ParsedValue<typeof parseRadioWidgetV1>,
136-
): RadioWidget {
134+
widget: ParsedValue<typeof parseRadioWidgetV0>,
135+
): ParsedValue<typeof parseRadioWidgetV1> {
137136
const {options} = widget;
138137
const {noneOfTheAbove: _, ...rest} = options;
139138
return {
@@ -142,13 +141,11 @@ function migrateV0ToV1(
142141
options: {
143142
...rest,
144143
hasNoneOfTheAbove: false,
144+
noneOfTheAbove: undefined,
145145
},
146146
};
147147
}
148148

149-
export const parseRadioWidget: Parser<RadioWidget> = versionedWidgetOptions(
150-
2,
151-
parseRadioWidgetV2,
152-
)
149+
export const parseRadioWidget = versionedWidgetOptions(2, parseRadioWidgetV2)
153150
.withMigrationFrom(1, parseRadioWidgetV1, migrateV1ToV2)
154151
.withMigrationFrom(0, parseRadioWidgetV0, migrateV0ToV1).parser;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {summon} from "../general-purpose-parsers/test-helpers";
2+
3+
import type {parseRadioWidget} from "./radio-widget";
4+
import type {RadioWidget} from "../../data-schema";
5+
import type {ParsedValue} from "../parser-types";
6+
7+
type Parsed = ParsedValue<typeof parseRadioWidget>;
8+
summon<Parsed>() satisfies RadioWidget;

packages/perseus-core/src/parse-perseus-json/regression-tests/__snapshots__/parse-perseus-json-regression.test.ts.snap

+10
Original file line numberDiff line numberDiff line change
@@ -2996,6 +2996,7 @@ exports[`parseAndMigratePerseusItem given input-number-with-boolean-value.json r
29962996
"displayCount": null,
29972997
"hasNoneOfTheAbove": false,
29982998
"multipleSelect": false,
2999+
"noneOfTheAbove": undefined,
29993000
"numCorrect": 1,
30003001
"onePerLine": true,
30013002
"randomize": false,
@@ -6988,6 +6989,7 @@ exports[`parseAndMigratePerseusItem given number-line-with-empty-strings-in-labe
69886989
"displayCount": null,
69896990
"hasNoneOfTheAbove": false,
69906991
"multipleSelect": true,
6992+
"noneOfTheAbove": undefined,
69916993
"numCorrect": 1,
69926994
"onePerLine": true,
69936995
"randomize": true,
@@ -9258,6 +9260,7 @@ exports[`parseAndMigratePerseusItem given orderer-option-missing-widgets.json re
92589260
"displayCount": null,
92599261
"hasNoneOfTheAbove": false,
92609262
"multipleSelect": false,
9263+
"noneOfTheAbove": undefined,
92619264
"numCorrect": 1,
92629265
"onePerLine": true,
92639266
"randomize": false,
@@ -9775,6 +9778,7 @@ Anton Peffenhauser, *Foot-Combat Armor of Prince-Elector Christian I of Saxony (
97759778
"displayCount": null,
97769779
"hasNoneOfTheAbove": false,
97779780
"multipleSelect": false,
9781+
"noneOfTheAbove": undefined,
97789782
"numCorrect": 1,
97799783
"onePerLine": true,
97809784
"randomize": false,
@@ -9799,6 +9803,7 @@ Anton Peffenhauser, *Foot-Combat Armor of Prince-Elector Christian I of Saxony (
97999803
"displayCount": null,
98009804
"hasNoneOfTheAbove": false,
98019805
"multipleSelect": false,
9806+
"noneOfTheAbove": undefined,
98029807
"numCorrect": 0,
98039808
"onePerLine": true,
98049809
"randomize": false,
@@ -10056,6 +10061,7 @@ exports[`parseAndMigratePerseusItem given plotter-with-undefined-plotDimensions.
1005610061
"displayCount": null,
1005710062
"hasNoneOfTheAbove": false,
1005810063
"multipleSelect": false,
10064+
"noneOfTheAbove": undefined,
1005910065
"numCorrect": 1,
1006010066
"onePerLine": true,
1006110067
"randomize": true,
@@ -10152,6 +10158,7 @@ exports[`parseAndMigratePerseusItem given radio-choice-missing-content.json retu
1015210158
"displayCount": null,
1015310159
"hasNoneOfTheAbove": false,
1015410160
"multipleSelect": false,
10161+
"noneOfTheAbove": undefined,
1015510162
"numCorrect": 0,
1015610163
"onePerLine": true,
1015710164
"randomize": false,
@@ -10199,6 +10206,7 @@ exports[`parseAndMigratePerseusItem given radio-choice-missing-content.json retu
1019910206
"displayCount": null,
1020010207
"hasNoneOfTheAbove": false,
1020110208
"multipleSelect": false,
10209+
"noneOfTheAbove": undefined,
1020210210
"numCorrect": 0,
1020310211
"onePerLine": true,
1020410212
"randomize": false,
@@ -10246,6 +10254,7 @@ exports[`parseAndMigratePerseusItem given radio-choice-missing-content.json retu
1024610254
"displayCount": null,
1024710255
"hasNoneOfTheAbove": false,
1024810256
"multipleSelect": false,
10257+
"noneOfTheAbove": undefined,
1024910258
"numCorrect": 0,
1025010259
"onePerLine": true,
1025110260
"randomize": false,
@@ -10293,6 +10302,7 @@ exports[`parseAndMigratePerseusItem given radio-choice-missing-content.json retu
1029310302
"displayCount": null,
1029410303
"hasNoneOfTheAbove": false,
1029510304
"multipleSelect": false,
10305+
"noneOfTheAbove": undefined,
1029610306
"numCorrect": 0,
1029710307
"onePerLine": true,
1029810308
"randomize": false,

0 commit comments

Comments
 (0)