fix: improve types in FieldRegistry#8062
fix: improve types in FieldRegistry#8062maribethb merged 2 commits intoRaspberryPiFoundation:developfrom
Conversation
cpcallen
left a comment
There was a problem hiding this comment.
LGTM modulo a couple of nits, but I'd recommend additional changes be made in v11 if possible:
Stopped exporting the
FieldPrototype fromfield.js
- because we're only using it in that file now
- We were never exporting this from the top level of blockly, so this doesn't change the public API
I think this could be further tidied, with the remaining uses of FieldProto removed by changing the signatures of saveLegacyState and loadLegacyState to take just the needed methods needed (in a form such that the caller can pass the .prototype object:
protected saveLegacyState(prototype: `Pick<Field, 'saveState' | 'toXml'>): string | null
protected loadLegacyState(prototype: Pick<Field, 'loadState' | 'fromXml'>): boolean
The types in the registry are still pretty wrong. The registry claims that what we'll get back from the
getObjectfunction is aField. Unfortunately that's not correct. We'll get back aFieldconstructor, which as discussed… needs to be typed differently if we're actually referencing the constructor as a type.The thing we get back from the
getObjectmethod should be a field constructor and it should have afromJsonmethod. This is the same exact type as what we pass intoregister, which makes sense. So we cast to that same type. Ideally the registry's generic types would have used these constructor interfaces instead of lying and saying they returnFieldinstances. But fixing that is a bigger deal and would need to be fixed for other constructable objects, not just fields.
Fixing that seems like a worthwhile thing to do. Arguably it could be postponed until after v11, though: if the signatures are really that wrong then fixing them won't break any correct programs, even if the fix itself is nominally a breaking change.
| */ | ||
| static fromJson(_options: FieldConfig): Field { | ||
| throw new Error( | ||
| `Attempted to instantiate a field from the registry that hasn't defined a 'fromJson' method.`, |
There was a problem hiding this comment.
we are bikeshedding this in the team chat if you want to join.
This would be a breaking change and I don't think it's worth doing. There's little if any benefit to the developer but there is cost. We could inline the existing
It's worthwhile in the abstract in that it would be nice if our types were not lies. But this is a much too big refactor to fit in before the v11 release. I also think it would be a breaking change, at least potentially, because developers may have worked around our incorrect types using a variety of methods that may not work anymore. And I'm not sure how important this particular ts refactor is compared to others that we know we need to do, like cleaning up our usage of |
The basics
The details
Resolves
Fixes #8059, to the extent that we're going to fix it anyway
Proposed Changes
RegistrableFieldinterface that represents a constructor for a Field class or subclass, and requires that constructed class also have a staticfromJsonmethod.Blockly.fieldRegistry.RegistrableField.registerfunction. Any functions in the public API should use and return accessible types, for the benefit of developers using TS. This solves the original issue reported by code.org.RegistryOptionstype a bit.fromJsonmethod, because we are just passing this object directly into that function.FieldPrototype fromfield.jsfromJsonto the baseFieldclassfromJsonfromJsonmethod of FieldRegistry.Reason for Changes
Explained inline above.
Test Coverage
Added/fixed as necessary
Documentation
No
Additional Information
The types in the registry are still pretty wrong. The registry claims that what we'll get back from the
getObjectfunction is aField. Unfortunately that's not correct. We'll get back aFieldconstructor, which as discussed in the docs also linked above, needs to be typed differently if we're actually referencing the constructor as a type.The thing we get back from the
getObjectmethod should be a field constructor and it should have afromJsonmethod. This is the same exact type as what we pass intoregister, which makes sense. So we cast to that same type. Ideally the registry's generic types would have used these constructor interfaces instead of lying and saying they returnFieldinstances. But fixing that is a bigger deal and would need to be fixed for other constructable objects, not just fields.Also, the types of the
fromJsonmethod are also a bit sad, which is why theRegistryOptionstype has to allow unknown keys. Each subclass ofFieldhas its ownfromJsonmethod and this method takes its ownFooConfigOptionstype as a parameter, because each field has different things that need to be configured from json. If theFieldclass had the type of itsfromJsonmethod as a parameterized generic type, that would allow us to say "hey whatever Field subclass we're working with, it should callfromJsonwith its ownFieldConfigtype." Since we haven't done that, we can't type check the object passed tofromJsonin the registry.And, the
RegistryOptionsis a bit weird too. Your field'sfromJsonmethod better not expect atypeproperty in the options object. Because we call the registry'sfromJsonmethod with only one parameter (which should have atypeproperty) and then pass that same parameter directly into the field'sfromJsonmethod. That's a bit weird. I think ideally the field registry'sfromJsonmethod should have hadtypeas a separate parameter, so that we aren't mixing in additional properties into the actual Field's config. But too late for that now.