这是indexloc提供的服务,不要输入任何密码
Skip to content

proposal: runtime_checks_with_js_types #59366

@srujzs

Description

@srujzs

runtime_checks_with_js_types

Description

These are lints that will make it easier to write and use JS interop code such that it can be deployed on dart2wasm without a divergence in semantics.

Details

Due to the difference in runtime types of the extension types ("JS types") we expose in dart:js_interop, casts and checks using is and as will almost certainly give different behavior depending on the platform.

A simple example is JSString. On dart2wasm, the representation type is a JSValue, while on the JS compilers it is String.

I go into a little more detail on this here: #59310. I chose to file a separate proposal, because even though there is heavy overlap, that proposal might not be sufficient.

Kind

Guard against errors and support platform compatibility.

Bad Examples

JSString x = ...;
x is String;

true on JS backends, false in dart2wasm. The reverse case is the same result.

JSAny x = ...;
x is JSString;

Always true on dart2wasm, only true if the value actually is a JS string value on the JS backends. Note that this may differ from the proposal in #59310, as this is check doesn't introduce or eliminate an extension type as it's a "downcheck" between two extension types.

List<dynamic> x = ...;
x is List<JSString>;

Depends on the initialization of x! #59310 does catch instances where we eliminate the extension type, so that'll combat the frequency of such checks. I think we should lint on this (whereas maybe not with a cast?) as this is likely to do more harm than good.

List<List<String>> x = ...;
x is List<JSString>;

This is always false on both compilers, but we can still lint, telling users they shouldn't do an is check where a JS type is compared against a non-JS type.

Object x = ...;
x is JSString;
x is List<JSString>;

This is a "downcheck" so we should likely lint. However, the reverse e.g. JSString is Object should probably not be linted, as that's statically true since JSString <: Object.

Good Examples

Add a few examples that demonstrate a “good” adoption of this lint’s principle.

JSString x = ...;
x is JSAny;

This is the reverse of the check above. This is okay because this is statically true as JSString <: JSAny. In the implementation, we should actually verify that though.

List<JSAny> x = ...;
x is CustomList<JSAny>;

Comparisons between JS types where the type is the same should be fine. The above check is not trivial either, as there is a useful check being done (List is a CustomList).

List<JSAny> x = ...;
x is List<JSAny?>;

Checking for nullability is interesting. I think this should be okay as the nullability should not be platform-dependent.

JSAny x = ...;
x as JSString;

Almost all the as cases are equivalent to the is cases, except for this one. This shouldn’t be a lint as it’s a legitimate downcast. If users know that JSAny is a JS string value, a cast is safe and the only way they can get a JSString. It may lead to different runtime semantics if users aren't careful, but we don't want to be in the way of a legitimate cast. Analysis with generics is similar e.g. List<JSAny> as List<JSString> is okay.

Discussion

We may choose to extend these lints further to suggest to users that they should use a conversion function when the intent is obvious. For example, String as JSString is an obvious candidate where we should tell users to use .toJS from dart:js_interop instead (and toDart in the reverse case). This would require a bit more inspection on the types involved.

In general, is is more dangerous and consistently wrong than as, so we may relax linting on casts versus checks.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions