Skip to content

Commit e511ead

Browse files
authored
#3180. Add isA tests. Part 2. (#3271)
Add `isA` tests. Part 2.
1 parent d3bb9c1 commit e511ead

File tree

7 files changed

+373
-0
lines changed

7 files changed

+373
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// @assertion bool isA<T extends JSAny?>()
6+
/// Whether this [JSAny?] is an instance of the JavaScript type that is declared
7+
/// by `T`.
8+
/// ...
9+
/// If T is a non-primitive JS type like [JSArray] or an interop extension type
10+
/// on one, this uses an instanceof check using the name or the `@JS` rename of
11+
/// the given type like `instanceOfString('Array')`. Note that if you rename the
12+
/// library using the `@JS` annotation, this uses the rename in the instanceof
13+
/// check like `instanceOfString('library1.JSClass')`.
14+
///
15+
/// @description Checks that `isA<JSArray>` returns `true` for an extension type
16+
/// on `JSArray`. Test renaming to `Array`.
17+
/// @author [email protected]
18+
19+
import 'dart:js_interop';
20+
import 'dart:js_interop_unsafe';
21+
import '../../../Utils/expect.dart';
22+
import '../js_utils.dart';
23+
24+
@JS("Array")
25+
extension type MyArray(JSArray ar) implements JSArray {}
26+
27+
main() {
28+
MyArray ma1 = MyArray(JSArray());
29+
globalContext["ma1"] = ma1;
30+
Expect.isTrue(globalContext["ma1"].isA<JSArray>());
31+
Expect.isFalse(globalContext["ma1"].isA<JSNumber>());
32+
Expect.isTrue(globalContext["ma1"].isA<MyArray>());
33+
34+
eval("globalThis.s = 'JS String';");
35+
JSString jsS = globalContext["s"] as JSString;
36+
JSArray<JSString> jsArr = JSArray<JSString>.withLength(1);
37+
jsArr[0] = jsS;
38+
MyArray ma2 = MyArray(jsArr);
39+
globalContext["ma2"] = ma2;
40+
Expect.isTrue(globalContext["ma2"].isA<JSArray<JSString>>());
41+
Expect.isTrue(globalContext["ma2"].isA<JSArray<JSNumber>>());
42+
Expect.isTrue(globalContext["ma2"].isA<MyArray>());
43+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// @assertion bool isA<T extends JSAny?>()
6+
/// Whether this [JSAny?] is an instance of the JavaScript type that is declared
7+
/// by `T`.
8+
/// ...
9+
/// If T is a non-primitive JS type like [JSArray] or an interop extension type
10+
/// on one, this uses an instanceof check using the name or the `@JS` rename of
11+
/// the given type like `instanceOfString('Array')`. Note that if you rename the
12+
/// library using the `@JS` annotation, this uses the rename in the instanceof
13+
/// check like `instanceOfString('library1.JSClass')`.
14+
///
15+
/// @description Checks that `isA<>` returns `true` for an appropriate interop
16+
/// type from a library.
17+
/// @author [email protected]
18+
19+
@JS("lib1")
20+
library;
21+
22+
import 'dart:async';
23+
import 'dart:js_interop';
24+
import 'dart:js_interop_unsafe';
25+
import '../../../Utils/expect.dart';
26+
import '../js_utils.dart';
27+
28+
final completer = Completer<String>();
29+
30+
void complete(String value) {
31+
completer.complete(value);
32+
}
33+
34+
@JS("A")
35+
extension type ET1(JSObject o) implements JSObject {
36+
external int id;
37+
external String name;
38+
}
39+
40+
main() {
41+
globalContext["complete"] = complete.toJS;
42+
eval(r'''
43+
(async () => {
44+
// This is path to the module on tryjobs. May not work locally.
45+
globalThis.lib1 = await import('/root_dart/tests/co19/src/LibTest/js_interop/module.js');
46+
globalThis.objA = new lib1.A(42, "A form JS");
47+
})().then(function(v) {
48+
globalThis.complete("");
49+
});
50+
''');
51+
asyncStart();
52+
completer.future.then((_) {
53+
ET1 et1 = globalContext["objA"] as ET1;
54+
Expect.isTrue(et1.isA<ET1>()); // ET1 interops with lib1.A
55+
asyncEnd();
56+
});
57+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// @assertion bool isA<T extends JSAny?>()
6+
/// Whether this [JSAny?] is an instance of the JavaScript type that is declared
7+
/// by `T`.
8+
/// ...
9+
/// To determine the JavaScript constructor to use as the second operand in the
10+
/// `instanceof` check, this function uses the JavaScript name associated with
11+
/// the extension type, which is either the argument given to the @JS annotation
12+
/// or the Dart declaration name. So, if you had an interop extension type
13+
/// `JSClass` that wraps [JSArray] without a rename, this does an
14+
/// `instanceOfString('JSClass')` check and not an `instanceOfString('Array')`
15+
/// check.
16+
///
17+
/// There are two exceptions to this rule. The first exception is [JSTypedArray].
18+
/// As `TypedArray` does not exist as a property in JavaScript, this does some
19+
/// prototype checking to make `isA<JSTypedArray>` do the right thing. The other
20+
/// exception is [JSAny]. If you do a `isA<JSAny>` check, it will only do a
21+
/// `null` check.
22+
///
23+
/// @description Checks that `isA<JSTypedArray>` returns `true` for a
24+
/// `JSTypedArray`.
25+
/// @author [email protected]
26+
27+
import 'dart:js_interop';
28+
import '../../../Utils/expect.dart';
29+
30+
main() {
31+
test(JSFloat32Array());
32+
test(JSFloat64Array());
33+
test(JSInt16Array());
34+
test(JSInt32Array());
35+
test(JSInt8Array());
36+
test(JSUint16Array());
37+
test(JSUint32Array());
38+
test(JSUint8Array());
39+
test(JSUint8ClampedArray());
40+
}
41+
42+
test(JSTypedArray ar) {
43+
Expect.isFalse(ar.isA<JSArray>());
44+
Expect.isTrue(ar.isA<JSTypedArray>());
45+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// @assertion bool isA<T extends JSAny?>()
6+
/// Whether this [JSAny?] is an instance of the JavaScript type that is declared
7+
/// by `T`.
8+
/// ...
9+
/// To determine the JavaScript constructor to use as the second operand in the
10+
/// `instanceof` check, this function uses the JavaScript name associated with
11+
/// the extension type, which is either the argument given to the @JS annotation
12+
/// or the Dart declaration name. So, if you had an interop extension type
13+
/// `JSClass` that wraps [JSArray] without a rename, this does an
14+
/// `instanceOfString('JSClass')` check and not an `instanceOfString('Array')`
15+
/// check.
16+
///
17+
/// There are two exceptions to this rule. The first exception is [JSTypedArray].
18+
/// As `TypedArray` does not exist as a property in JavaScript, this does some
19+
/// prototype checking to make `isA<JSTypedArray>` do the right thing. The other
20+
/// exception is [JSAny]. If you do a `isA<JSAny>` check, it will only do a
21+
/// `null` check.
22+
///
23+
/// @description Checks that `isA<JSAny>` returns `true` for any non-null value.
24+
/// @author [email protected]
25+
26+
import 'dart:js_interop';
27+
import 'dart:js_interop_unsafe';
28+
import '../../../Utils/expect.dart';
29+
import '../js_utils.dart';
30+
31+
main() {
32+
eval(r'''
33+
globalThis.i = 1;
34+
globalThis.f = 3.14;
35+
globalThis.s = "JS String";
36+
globalThis.b = true;
37+
globalThis.bi = 42n;
38+
globalThis.sm = Symbol();
39+
globalThis.n = null;
40+
globalThis.u = undefined;
41+
''');
42+
Expect.isTrue(JSObject().isA<JSAny>());
43+
Expect.isTrue(globalContext["i"].isA<JSAny>());
44+
Expect.isTrue(globalContext["f"].isA<JSAny>());
45+
Expect.isTrue(globalContext["s"].isA<JSAny>());
46+
Expect.isTrue(globalContext["b"].isA<JSAny>());
47+
Expect.isTrue(globalContext["bi"].isA<JSAny>());
48+
Expect.isTrue(globalContext["sm"].isA<JSAny>());
49+
Expect.isFalse(globalContext["n"].isA<JSAny>());
50+
Expect.isFalse(globalContext["u"].isA<JSAny>());
51+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// @assertion bool isA<T extends JSAny?>()
6+
/// Whether this [JSAny?] is an instance of the JavaScript type that is declared
7+
/// by `T`.
8+
/// ...
9+
/// Using this method with a `T` that has an object literal constructor will
10+
/// result in an error as you likely want to use [JSObject] instead.
11+
///
12+
/// @description Checks that it is a compile-time error if a type with an object
13+
/// literal constructor is used as a type argument of `isA`.
14+
/// @author [email protected]
15+
16+
import 'dart:js_interop';
17+
18+
extension type ET1._(JSObject _) implements JSObject {
19+
external ET1({int id, String name});
20+
}
21+
22+
extension type ET2(JSObject _) implements JSAny {
23+
external ET2.n({int id, String name});
24+
}
25+
26+
@JS("Array")
27+
extension type ET3(JSArray _) implements JSObject {
28+
external ET3.n({int id, String name});
29+
}
30+
31+
test(JSAny v) {
32+
v.isA<ET1>();
33+
// ^^^
34+
// [analyzer] unspecified
35+
// [web] unspecified
36+
37+
v.isA<ET2>();
38+
// ^^^
39+
// [analyzer] unspecified
40+
// [web] unspecified
41+
42+
v.isA<ET3>();
43+
// ^^^
44+
// [analyzer] unspecified
45+
// [web] unspecified
46+
}
47+
48+
main() {
49+
print(test);
50+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// @assertion bool isA<T extends JSAny?>()
6+
/// Whether this [JSAny?] is an instance of the JavaScript type that is declared
7+
/// by `T`.
8+
/// ...
9+
/// Using this method with a `T` that wraps a primitive JS type will result in
10+
/// an error telling you to use the primitive JS type instead.
11+
///
12+
/// @description Checks that it is a compile-time error if an extension type on
13+
/// JS primitive types is used as a type argument of `isA`.
14+
/// @author [email protected]
15+
16+
import 'dart:js_interop';
17+
18+
extension type MyAny(JSAny _) implements JSAny {}
19+
20+
extension type MyNumber(JSNumber _) implements JSNumber {}
21+
22+
extension type MyString(JSString _) implements JSString {}
23+
24+
extension type MyBoolean(JSBoolean _) implements JSBoolean {}
25+
26+
extension type MyBigInt(JSBigInt _) implements JSBigInt {}
27+
28+
extension type MySymbol(JSSymbol _) implements JSSymbol {}
29+
30+
extension type MyObject(JSObject _) implements JSObject {}
31+
32+
test(JSAny v) {
33+
v.isA<MyAny>();
34+
v.isA<MyObject>();
35+
v.isA<JSNumber>();
36+
v.isA<MyNumber>();
37+
// ^^^
38+
// [analyzer] unspecified
39+
// [web] unspecified
40+
41+
v.isA<MyString>();
42+
// ^^^
43+
// [analyzer] unspecified
44+
// [web] unspecified
45+
46+
v.isA<MyBoolean>();
47+
// ^^^
48+
// [analyzer] unspecified
49+
// [web] unspecified
50+
51+
v.isA<MyBigInt>();
52+
// ^^^
53+
// [analyzer] unspecified
54+
// [web] unspecified
55+
56+
v.isA<MySymbol>();
57+
// ^^^
58+
// [analyzer] unspecified
59+
// [web] unspecified
60+
}
61+
62+
main() {
63+
print(test);
64+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// @assertion bool isA<T extends JSAny?>()
6+
/// Whether this [JSAny?] is an instance of the JavaScript type that is declared
7+
/// by `T`.
8+
/// ...
9+
/// Using this method with a `T` that wraps a primitive JS type will result in
10+
/// an error telling you to use the primitive JS type instead.
11+
///
12+
/// @description Checks that it is a compile-time error if an extension type on
13+
/// JS primitive types is used as a type argument of `isA`. Test extension types
14+
/// with rename.
15+
/// @author [email protected]
16+
17+
import 'dart:js_interop';
18+
19+
@JS("Number")
20+
extension type MyNumber(JSNumber _) implements JSNumber {}
21+
22+
@JS("String")
23+
extension type MyString(JSString _) implements JSString {}
24+
25+
@JS("Boolean")
26+
extension type MyBoolean(JSBoolean _) implements JSBoolean {}
27+
28+
@JS("BigInt")
29+
extension type MyBigInt(JSBigInt _) implements JSBigInt {}
30+
31+
@JS("Symbol")
32+
extension type MySymbol(JSSymbol _) implements JSSymbol {}
33+
34+
test(JSAny v) {
35+
v.isA<MyNumber>();
36+
// ^^^
37+
// [analyzer] unspecified
38+
// [web] unspecified
39+
40+
v.isA<MyString>();
41+
// ^^^
42+
// [analyzer] unspecified
43+
// [web] unspecified
44+
45+
v.isA<MyBoolean>();
46+
// ^^^
47+
// [analyzer] unspecified
48+
// [web] unspecified
49+
50+
v.isA<MyBigInt>();
51+
// ^^^
52+
// [analyzer] unspecified
53+
// [web] unspecified
54+
55+
v.isA<MySymbol>();
56+
// ^^^
57+
// [analyzer] unspecified
58+
// [web] unspecified
59+
}
60+
61+
main() {
62+
print(test);
63+
}

0 commit comments

Comments
 (0)