-
Notifications
You must be signed in to change notification settings - Fork 350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SSS: Improve types for validation #2002
base: feature/client-validation
Are you sure you want to change the base?
Conversation
Size Change: +115 B (+0.01%) Total Size: 1.27 MB
ℹ️ View Unchanged
|
91dd867
to
c619dd8
Compare
d84a790
to
8629c57
Compare
| PerseusSorterRubric | ||
| PerseusTableRubric; | ||
|
||
export type UserInput = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is moved down below all of the Rubric/Scoring Data types together with the various widget UserInput types. #organizing
export interface RubricRegistry { | ||
categorizer: PerseusCategorizerScoringData; | ||
"cs-program": PerseusCSProgramRubric; | ||
// definition: PerseusDefinitionScoringData; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really am not a fan of how this works right now. The fact that this is not a full listing of all widgets because we don't score all widgets worries me, but I don't think it makes sense to add a type for all widgets just to make this "whole".
I'm tempted to leave it like this for now and as we work further hopefully something resolves to a better solution.
Also open to ideas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not all widgets have ScoringData, why don't we just remove the inner comments and add a comment to the whole interface (just above line 241) saying that? We could keep a list of which widgets currently don't have scoring data in that comment so it doesn't muddy the interface so to speak. Or is there a reason why we should have an entry for every widget in this interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My vote is remove entries for widgets we don't score.
npm Snapshot: PublishedGood news!! We've packaged up the latest commit from this PR (cf7d5c1) and published it to npm. You Example: yarn add @khanacademy/perseus@PR2002 If you are working in Khan Academy's webapp, you can run: ./dev/tools/bump_perseus_version.sh -t PR2002 |
// We can use a RubricMap as a ValidationDataMap | ||
0 as any as RubricMap satisfies ValidationDataMap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May want to update these to ScoringMap
since we're removing the rubric
terminology.
// A utility type that constructs a widget map from a "registry interface". | ||
// The keys of the registry should be the widget type (aka, "categorizer" or | ||
// "radio", etc) and the value should be the option type stored in the value | ||
// of the map. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you provide an example of the output from this function? Or an example usage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. So I've started developing the idea of a type registry (because we have full widget options types, scoring data types, frontend rendering types). Each of these is map of widget type to the option type for that widget.
If you look at the usage in this PR, we use the PerseusWidgetTypes
interface to create the PerseusWidgetsMap
type.
PerseusWidgetTypes
looks like this (shortened a bit here):
export interface PerseusWidgetTypes {
categorizer: CategorizerWidget;
"cs-program": CSProgramWidget;
definition: DefinitionWidget;
...
}
We use the helper:
export type PerseusWidgetsMap = MakeWidgetMap<PerseusWidgetTypes>;
And out comes the following type:
type PerseusWidgetsMap = {
[x: `categorizer ${number}`]: CategorizerWidget;
[x: `cs-program ${number}`]: CSProgramWidget;
[x: `definition ${number}`]: DefinitionWidget;
...
}
So you can think of this helper as a sort of type "generator". It takes a registry of widget types and generates a map type where the ID of the map is a widget id and the value is the correct option type.
| PerseusTableUserInput; | ||
|
||
export type UserInputMap = {[widgetId: string]: UserInput}; | ||
export interface RubricRegistry { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be ScoringDataRegistry? Doesn't have the same ring, but we're trying to move away from the rubric
vocab, right? Also, should the Rubric types below be changed to ScoringData types or will we just handle that after landing my renaming PR when we get back? Might make sense to plan the order in which we land PRs, though it might make sense just to end the week with a fully merged feature branch with all our changes combined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I plan to land this PR after your scoring name changes... so I'll be sure to update the instances of Rubric in this PR before landing.
import type {RubricMap, ValidationDataMap} from "../validation.types"; | ||
|
||
// We can use a 'widgets' map from a PerseusRenderer as a ValidationDataMap | ||
0 as any as PerseusRenderer["widgets"] satisfies ValidationDataMap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 0 as any as
business really confused me when I first saw it; even now that I now what it's for, I still don't understand what it actually works. I honestly don't feel super comfortable having it in the code because it's both unclear and feels hacky. That being said, I'm fine if we keep it but I'd like it if we could link to some documentation on this pattern (or if it doesn't exist we can write our own).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a pattern I saw in Ben's parsing code changes.
I've tried a different approach that will hopefully be clearer. My concern is that the two types being tested here (PerseusRenderer["widgets"]
and ScoringDataMap
) must remain compatible with ValidatioDataMap
.
I don't another way to do that.
export interface RubricRegistry { | ||
categorizer: PerseusCategorizerScoringData; | ||
"cs-program": PerseusCSProgramRubric; | ||
// definition: PerseusDefinitionScoringData; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My vote is remove entries for widgets we don't score.
* share functionality that understands how to traverse maps of `widget id` to | ||
* `options`. | ||
*/ | ||
export type RubricMap = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So overall the point of this PR seems to be aimed at more closely matching widget-specific data to the widget ID?
Doesn't this reinforce the thing you and Nicole were against - tightly binding the widget type
and the widget id
? Like if we were to decide tomorrow that we were going to give all widgets UUIDs (which we should as we've seen in the GradedGroup issue), wouldn't that break this code?
I'm having trouble aligning the description of the PR and the ticket with the actual changes. This is like pre/post work for LEMS-2561?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this PR drifted from what I initially typed out as the PR description. I'll update the description. It's basically type work to make the types we already use (full widget options, Scoring Data, Validation Data) a bit more clear and well-typed.
I agree that I don't like us buying further into the mapping of widget ID to data in our various maps. Maybe we can chat about that in a future PR? I'm also happy to have pushback and do more work in this PR to isolate the Renderer and outer code knowing so much about widget option details.
export type RubricMap = { | ||
[Property in keyof RubricRegistry as `${Property} ${number}`]: { | ||
type: Property; | ||
static?: boolean; | ||
options: RubricRegistry[Property]; | ||
}; | ||
}; | ||
|
||
export type Rubric = RubricRegistry[keyof RubricRegistry]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also pointing out the use of Rubric instead of ScoringData here as well.
// A union type of all the widget user input types | ||
export type UserInput = UserInputRegisry[keyof UserInputRegisry]; | ||
|
||
/** | ||
* A map of widget IDs to user input types (strongly typed based on the format | ||
* of the widget ID). | ||
*/ | ||
export type UserInputMap = MakeWidgetMap<UserInputRegisry>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you only use the asterisk comments in certain situations? Just wondering when it should be the //
version or the /**
version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
930bb96
to
7ce25ce
Compare
Summary:
This PR begins working out the types for Scoring, Validation, and how they relate to our full widget options types. There are currently three "trees" of types that are shaped as a map of
widgetId
to something. They are:PerseusRenderer
)Finally, there is also a widget map known as User Input. This map is a map of widget ids from the item to the user input the learner has entered so far.
Issue: LEMS-2561
Test plan:
yarn typecheck
(especially, the newvalidation.typetest.ts
file!)yarn test
(just to be sure)