-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Type check and promotion for js interop #52030
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
Comments
@jodinathan, it isn't obvious to me how to interpret this issue as a Dart language proposal. Perhaps it fits better into a repository which is concerned with the design of the upcoming version of JS interop? @srujzs, do you have an idea about the best placement for issues like this? |
@eernstg yeah, agreed. |
I think if the goal is just to get nice We've thought about similar things with our interop design, because unlike My favorite idea for this is a new language feature that allows users to add an operator This can go in the SDK under the tags area-web and area-jsinterop if a language feature is out of scope in the mediumish-term. |
Thanks, @srujzs! I think it makes a lot of sense to have an overall approach where JS interop (and interop in general) uses Dart types to express a level of static guarantees which is actually backed up by the underlying semantics, and then provide access to features like a JavaScript In any case, and similarly to FFI, this discussion is specific to the behavior of some Dart programs on some platforms, which means that it won't be considered part of Dart-the-language, and it won't be part of the language specification. This supports the placement of this kind of discussion in the SDK repo, with said labels. I'll transfer the issue and add the labels. |
I think what remains open from the language perspective is:
For example, imagine: @JS('JSX')
inline class X {
JSObject rep;
static operator is(o) => js_interop.instanceof(o, 'JSX');
}
...
foo(o) {
if (o is X) { // this generates code like: o is JSObject && X.is(o)
// do we promote o to X here?
}
} Today the alternative is to write this: @JS('JSX')
inline class X {
JSObject rep;
static operator is(o) => o is JSObject && js_interop.instanceof(o, 'JSX');
}
...
foo(o) {
if (X.is(o)) {
var ox = X(o);
// use ox, instead of o
}
} Given that there is a simple alternative, we are not yet convinced that we want this feature. It looks convenient, but we worry that there are hidden costs with it which may be higher than the benefits. |
If we want that, I think we should introduce a language repo issue for a user-defined However, we've discussed this kind of feature several times during the evolution of inline classes, and it's a path with many obstacles. For example, we may be able to run specialized code for On the other hand, we may already have a partial solution (I renamed @JS('JSC')
inline class C implements JSObject {
final JSObject rep;
bool get isValid => js_interop.instanceof(this, 'JSC');
}
...
void foo(JSObject o) {
if (o is C && o.isValid) {
... // `o` is promoted to `C` here.
}
} The clause So what I've done in the example above is
This means that promotion to an inline type can be used by a developer to express the intent to access a given object using that inline type, and the validation code can be executed in order to ensure that this kind of access is valid. Consider the case where the inline type occurs as a subterm of another type: // `inline class C` declared as before.
void baz(List<JSObject> os) {
if (os is List<C>) { // Promote the list as a whole.
for (var o in os) {
if (!o.isValid) continue; // Validate each element in the list.
... // `o` is promoted to `C` here.
}
}
} This illustrates that it is straightforward, in some cases, to handle the higher-order usage where the inline type occurs as part of some other type (promotion "just works"), but it is obviously going to be difficult to use a lint or warning to ensure that any kind of validation is actually performed whenever we have this kind of indirect promotions to an inline type. The case where an inline type is the value of a type argument is basically only relevant as seen from the call site: // `inline class C` declared as before.
void foo(JSObject o) {
var c = bar<C>(o);
... // `c` is a `C` typed way to access `o`.
}
Y bar<Y extends JSObject>(JSObject o) {
if (o is Y) return o; // But we have no way to know that it should be validated!
throw "Ignore other situations";
} At run time, in the body of During the evolution of the inline class feature we discussed several designs where it could be supported by the runtime that type tests like |
I am not sure if adding an The great thing about Dart is that it isn't magical as other languages. However, JS interop is magical by nature, so we could do anything here to make it easier for clients. |
A feature of the Typings package is that it makes predicate (type guards) in TS (
function isFoo(arg) : arg is Foo;
) a method that returns the object promoted instead of a bool.While creating it I thought that we may have what is needed to make a type check system for js interop:
instanceof
to checkWe could have an argument in the
@JS
or@staticInterop
annotation that would map the types to the checks.Something like
Then when a type check is used (
bar is Foo
) the dart analyzer and compiler would follow some logic like in:The text was updated successfully, but these errors were encountered: