invalid_ runtime_ check_ with_ js_ interop_ types
Learn about the invalid_runtime_check_with_js_interop_types linter rule.
Avoid runtime type tests with JS interop types where the result may not be platform-consistent.
Details
#DON'T use is checks where the type is a JS interop type.
DON'T use is checks where the type is a generic Dart type that has JS
interop type arguments.
DON'T use is checks with a JS interop value.
dart:js_interop types have runtime types that are different based on whether
you are compiling to JS or to Wasm. Therefore, runtime type checks may result in
different behavior. Runtime checks also do not necessarily check that a JS
interop value is a particular JavaScript type.
BAD:
extension type HTMLElement(JSObject o) {}
extension type HTMLDivElement(JSObject o) implements HTMLElement {}
void compute(JSAny a, bool b, List<JSObject> lo, List<String> ls, JSObject o,
HTMLElement e) {
a is String; // LINT, checking that a JS value is a Dart type
b is JSBoolean; // LINT, checking that a Dart value is a JS type
a is JSString; // LINT, checking that a JS value is a different JS interop
// type
o is JSNumber; // LINT, checking that a JS value is a different JS interop
// type
lo is List<String>; // LINT, JS interop type argument and Dart type argument
// are incompatible
ls is List<JSString>; // LINT, Dart type argument and JS interop type argument
// are incompatible
lo is List<JSArray>; // LINT, comparing JS interop type argument with
// different JS interop type argument
lo is List<JSNumber>; // LINT, comparing JS interop type argument with
// different JS interop type argument
o is HTMLElement; // LINT, true because both are JSObjects but doesn't check
// that it's a JS HTMLElement
e is HTMLDivElement; // LINT, true because both are JSObjects but doesn't
// check that it's a JS HTMLDivElement
}
Prefer using JS interop helpers like isA from dart:js_interop to check the
underlying type of JS interop values.
GOOD:
extension type HTMLElement(JSObject o) implements JSObject {}
extension type HTMLDivElement(JSObject o) implements HTMLElement {}
void compute(JSAny a, List<JSAny> l, JSObject o, HTMLElement e, Object obj) {
a.isA<JSString>; // OK, uses JS interop to check it is a JS string
l[0].isA<JSString>; // OK, uses JS interop to check it is a JS string
o.isA<HTMLElement>(); // OK, uses JS interop to check `o` is an HTMLElement
e.isA<HTMLDivElement>(); // OK, uses JS interop to check `e` is an
// HTMLDivElement
obj.isA<JSObject>(); // OK, uses JS interop to check `obj` is a JSObject
}
DON'T use as to cast a JS interop value to an unrelated Dart type or an
unrelated Dart value to a JS interop type.
DON'T use as to cast a JS interop value to a JS interop type represented
by an incompatible dart:js_interop type.
BAD:
extension type Window(JSObject o) {}
void compute(String s, JSBoolean b, Window w, List<String> l,
List<JSObject> lo) {
s as JSString; // LINT, casting Dart type to JS interop type
b as bool; // LINT, casting JS interop type to Dart type
b as JSNumber; // LINT, JSBoolean and JSNumber are incompatible
b as Window; // LINT, JSBoolean and JSObject are incompatible
w as JSBoolean; // LINT, JSObject and JSBoolean are incompatible
l as List<JSString>; // LINT, casting Dart value with Dart type argument to
// Dart type with JS interop type argument
lo as List<String>; // LINT, casting Dart value with JS interop type argument
// to Dart type with Dart type argument
lo as List<JSBoolean>; // LINT, casting Dart value with JS interop type
// argument to Dart type with incompatible JS interop
// type argument
}
Prefer using dart:js_interop conversion methods to convert a JS interop value
to a Dart value and vice versa.
GOOD:
extension type Window(JSObject o) {}
extension type Document(JSObject o) {}
void compute(String s, JSBoolean b, Window w, JSArray<JSString> a,
List<String> ls, JSObject o, List<JSAny> la) {
s.toJS; // OK, converts the Dart type to a JS type
b.toDart; // OK, converts the JS type to a Dart type
a.toDart; // OK, converts the JS type to a Dart type
w as Document; // OK, but no runtime check that `w` is a JS Document
ls.map((e) => e.toJS).toList(); // OK, converts the Dart types to JS types
o as JSArray<JSString>; // OK, JSObject and JSArray are compatible
la as List<JSString>; // OK, JSAny and JSString are compatible
(o as Object) as JSObject; // OK, Object is a supertype of JSAny
}
**DON'T** use a JS interop type in a catch clause. If the code in the try
block is expected to throw a JS value, don't use a type at all in the
catch clause.
**BAD:**
```dart
@JS()
external void throwSomething();
extension type JSError(JSObject _) implements JSObject {}
void compute() {
try {
throwSomething();
} on JSString catch (e) { // LINT, checking that the caught value is a JS type
print('string $e');
} on JSError catch (e) { // LINT, checking that the caught value is a JS
// interop type
print('JSError: $e');
} on List<JSNumber> catch (e) { // LINT, type contains a JS interop type
print('List<JSNumber>: $e');
} on bool catch (e) { // NO LINT, but may be platform-dependent
print('bool: $e');
}
}
Use isA from dart:js_interop to check if a value is a JS interop type
within the catch block. If the code in the try block is expected to throw
a JS value, leave the catch clause untyped and check for all JS interop
types first using isA before checking for Dart types.
GOOD:
@JS()
external void throwSomething();
extension type JSError(JSObject _) implements JSObject {}
void compute() {
try {
throwSomething();
} catch (e) {
if (e.isA<JSString>()) { // OK, uses JS interop to check it is a JS string
print('string: $e');
} else if (e.isA<JSError>()) { // OK, uses JS interop to check it is a JSError
print('Error: $e');
} else if (e.isA<JSAny>()) { // OK, checks all interop types before Dart types
print('JSAny: $e');
} else if (e is bool) { // OK, `e` can't be a JS value
print('boolean: $e');
}
}
}
Enable
#
To enable the invalid_runtime_check_with_js_interop_types rule, add invalid_runtime_check_with_js_interop_types
under
linter > rules in your analysis_options.yaml
file:
linter:
rules:
- invalid_runtime_check_with_js_interop_types
If you're instead using the YAML map syntax to configure linter rules,
add invalid_runtime_check_with_js_interop_types: true under linter > rules:
linter:
rules:
invalid_runtime_check_with_js_interop_types: true
Unless stated otherwise, the documentation on this site reflects Dart 3.11.0. Report an issue.