sdk
sdk copied to clipboard
`@anonymous` classes should not be rename-able or have `static` members
Currently it's possible to rename and add external static members in an @anonymous class (with or without the @staticInterop annotation):
@JS('Rename')
@anonymous
// @staticInterop
class AnonymousClass {
external static int get foo;
}
The semantics are as expected e.g. calling AnonymousClass.foo will result in Rename.foo. Should we disallow this? It seems like an anti-pattern to use @anonymous classes for this purpose instead of JS classes without the @anonymous, but it works as one might expect. The @staticInterop versions have the same lowering but since they're lowered through the transformer, they are type-checked.
This would be a breaking change, and there are internal uses of this (but seem to be limited), so it would require a migration.
Interop extension types do not have this issue as we don't distinguish types based on whether they have an object literal constructor or not, so this only affects @JS classes.
cc @nshahan
Can you share more about the motivation behind banning renames and banning static members?
I interpret @anonymous to mean this is a JavaScript object literal without a specific name, but static implies that the member is accessible through a namespace. To me that makes @anonymous and the static keyword seem to feel at odds with each other.
I think I'm probably in favor of cleaning up the known uses and making this pattern an error, especially if it would make any future migration away from @JS interop easier by being more predictable.
It's mostly just a source of confusion. Using @anonymous tells me you plan on creating an arbitrary object literal that is defined by its shape, but using a rename or a static member tells me you care about the JS name of this type, which the object literal constructor doesn't even use. If we disallow non-object literal constructors in an @anonymous class (which we do), this argument is the logical extension of that. The disallowing might be due to other legacy reasons, however.
In fairness, this logic could also be applied to interop extension types, as the same capabilities apply there even though we don't distinguish between named and object literal interop extension types.
That being said, can I imagine some possible reason why people may want to do this? Sure, and maybe we don't want to stop them from doing that, even if it seems confusing.
That being said, can I imagine some possible reason why people may want to do this? Sure, and maybe we don't want to stop them from doing that, even if it seems confusing.
Are you imagining something that can't be done any other way that might lead to less confusion?
Are you imagining something that can't be done any other way that might lead to less confusion?
In the package:js case? No, not really. In the case with extension types where you can have both a named constructor and an object literal constructor? Maybe? I can see it as a way to mock an instance without needing a separate type.