Merge branch 'master' into raw-test-api
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 0f13fc8..26f3091 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -2,6 +2,7 @@
 analyzer:
   language:
     strict-casts: true
+    strict-raw-types: true
   errors:
     # There are a number of deprecated members used through this package
     deprecated_member_use_from_same_package: ignore
diff --git a/integration_tests/spawn_hybrid/lib/emits_numbers.dart b/integration_tests/spawn_hybrid/lib/emits_numbers.dart
index 6429a85..a60ffa6 100644
--- a/integration_tests/spawn_hybrid/lib/emits_numbers.dart
+++ b/integration_tests/spawn_hybrid/lib/emits_numbers.dart
@@ -4,7 +4,7 @@
 
 import 'package:stream_channel/stream_channel.dart';
 
-void hybridMain(StreamChannel channel) {
+void hybridMain(StreamChannel<Object?> channel) {
   channel.sink
     ..add(1)
     ..add(2)
diff --git a/integration_tests/spawn_hybrid/other_package/lib/emits_numbers.dart b/integration_tests/spawn_hybrid/other_package/lib/emits_numbers.dart
index 6429a85..a60ffa6 100644
--- a/integration_tests/spawn_hybrid/other_package/lib/emits_numbers.dart
+++ b/integration_tests/spawn_hybrid/other_package/lib/emits_numbers.dart
@@ -4,7 +4,7 @@
 
 import 'package:stream_channel/stream_channel.dart';
 
-void hybridMain(StreamChannel channel) {
+void hybridMain(StreamChannel<Object?> channel) {
   channel.sink
     ..add(1)
     ..add(2)
diff --git a/integration_tests/spawn_hybrid/test/hybrid_test.dart b/integration_tests/spawn_hybrid/test/hybrid_test.dart
index 75f7250..d456133 100644
--- a/integration_tests/spawn_hybrid/test/hybrid_test.dart
+++ b/integration_tests/spawn_hybrid/test/hybrid_test.dart
@@ -295,7 +295,7 @@
     });
 
     group('closes the channel when the test finishes by default', () {
-      late StreamChannel channel;
+      late StreamChannel<Object?> channel;
 
       test('test 1', () {
         channel = spawnHybridCode('''
@@ -314,8 +314,8 @@
     });
 
     group('persists across multiple tests with stayAlive: true', () {
-      late StreamQueue queue;
-      late StreamSink sink;
+      late StreamQueue<Object?> queue;
+      late StreamSink<Object?> sink;
       setUpAll(() {
         var channel = spawnHybridCode('''
               import "package:stream_channel/stream_channel.dart";
diff --git a/integration_tests/spawn_hybrid/test/util/echos_message.dart b/integration_tests/spawn_hybrid/test/util/echos_message.dart
index 271b44f..ffe991a 100644
--- a/integration_tests/spawn_hybrid/test/util/echos_message.dart
+++ b/integration_tests/spawn_hybrid/test/util/echos_message.dart
@@ -4,7 +4,7 @@
 
 import 'package:stream_channel/stream_channel.dart';
 
-void hybridMain(StreamChannel channel, Object message) {
+void hybridMain(StreamChannel<Object?> channel, Object message) {
   channel.sink
     ..add(message)
     ..close();
diff --git a/integration_tests/spawn_hybrid/test/util/emits_numbers.dart b/integration_tests/spawn_hybrid/test/util/emits_numbers.dart
index 6429a85..a60ffa6 100644
--- a/integration_tests/spawn_hybrid/test/util/emits_numbers.dart
+++ b/integration_tests/spawn_hybrid/test/util/emits_numbers.dart
@@ -4,7 +4,7 @@
 
 import 'package:stream_channel/stream_channel.dart';
 
-void hybridMain(StreamChannel channel) {
+void hybridMain(StreamChannel<Object?> channel) {
   channel.sink
     ..add(1)
     ..add(2)
diff --git a/pkgs/checks/lib/src/checks.dart b/pkgs/checks/lib/src/checks.dart
index eb4f273..bca3071 100644
--- a/pkgs/checks/lib/src/checks.dart
+++ b/pkgs/checks/lib/src/checks.dart
@@ -312,10 +312,10 @@
   /// A reference to find the root context which this context is nested under.
   ///
   /// null only for the root context.
-  final _TestContext<dynamic>? _parent;
+  final _TestContext<void>? _parent;
 
   final List<_ClauseDescription> _clauses;
-  final List<_TestContext> _aliases;
+  final List<_TestContext<void>> _aliases;
 
   // The "a value" in "a value that:".
   final String _label;
@@ -340,7 +340,7 @@
         _clauses = [],
         _aliases = [];
 
-  _TestContext._alias(_TestContext original, this._value)
+  _TestContext._alias(_TestContext<void> original, this._value)
       : _parent = original,
         _clauses = original._clauses,
         _aliases = original._aliases,
@@ -442,7 +442,7 @@
   CheckFailure _failure(Rejection rejection) =>
       CheckFailure(rejection, () => _root.detail(this));
 
-  _TestContext get _root {
+  _TestContext<void> get _root {
     _TestContext<dynamic> current = this;
     while (current._parent != null) {
       current = current._parent!;
@@ -451,7 +451,7 @@
   }
 
   @override
-  FailureDetail detail(_TestContext failingContext) {
+  FailureDetail detail(_TestContext<void> failingContext) {
     assert(_clauses.isNotEmpty);
     final thisContextFailed =
         identical(failingContext, this) || _aliases.contains(failingContext);
@@ -511,14 +511,14 @@
 }
 
 abstract class _ClauseDescription {
-  FailureDetail detail(_TestContext failingContext);
+  FailureDetail detail(_TestContext<void> failingContext);
 }
 
 class _StringClause implements _ClauseDescription {
   final Iterable<String> Function() _expected;
   _StringClause(this._expected);
   @override
-  FailureDetail detail(_TestContext failingContext) =>
+  FailureDetail detail(_TestContext<void> failingContext) =>
       FailureDetail(_expected(), -1, -1);
 }
 
diff --git a/pkgs/checks/test/extensions/async_test.dart b/pkgs/checks/test/extensions/async_test.dart
index 2e9ed70..d9aad7f 100644
--- a/pkgs/checks/test/extensions/async_test.dart
+++ b/pkgs/checks/test/extensions/async_test.dart
@@ -16,7 +16,7 @@
     test('completes', () async {
       (await checkThat(_futureSuccess()).completes()).equals(42);
 
-      await _rejectionWhichCheck<Future>(
+      await _rejectionWhichCheck<Future<void>>(
         _futureFail(),
         it()..completes(),
         it()..single.equals('Threw <UnimplementedError>'),
@@ -28,13 +28,13 @@
           .has((p0) => p0.message, 'message')
           .isNull();
 
-      await _rejectionWhichCheck<Future>(
+      await _rejectionWhichCheck<Future<void>>(
         _futureSuccess(),
         it()..throws(),
         it()..single.equals('Did not throw'),
       );
 
-      await _rejectionWhichCheck<Future>(
+      await _rejectionWhichCheck<Future<void>>(
         _futureFail(),
         it()..throws<StateError>(),
         it()..single.equals('Is not an StateError'),
@@ -92,19 +92,19 @@
     test('emits', () async {
       (await checkThat(_countingStream(5)).emits()).equals(0);
 
-      await _rejectionWhichCheck<StreamQueue>(
+      await _rejectionWhichCheck<StreamQueue<int>>(
         _countingStream(0),
         it()..emits(),
         it()..single.equals('did not emit any value'),
       );
 
-      await _rejectionWhichCheck<StreamQueue>(
+      await _rejectionWhichCheck<StreamQueue<int>>(
         _countingStream(0),
         it()..emits(),
         it()..single.equals('did not emit any value'),
       );
 
-      await _rejectionWhichCheck<StreamQueue>(
+      await _rejectionWhichCheck<StreamQueue<int>>(
         _countingStream(1, errorAt: 0),
         it()..emits(),
         it()..single.equals('emitted an error instead of a value'),
@@ -114,7 +114,7 @@
     test('emitsThrough', () async {
       await checkThat(_countingStream(5)).emitsThrough(it()..equals(4));
 
-      await _rejectionWhichCheck<StreamQueue>(
+      await _rejectionWhichCheck<StreamQueue<int>>(
         _countingStream(4),
         it()..emitsThrough(it()..equals(5)),
         it()
@@ -125,7 +125,7 @@
     test('neverEmits', () async {
       await checkThat(_countingStream(5)).neverEmits(it()..equals(5));
 
-      await _rejectionWhichCheck<StreamQueue>(
+      await _rejectionWhichCheck<StreamQueue<int>>(
         _countingStream(6),
         it()..neverEmits(it()..equals(5)),
         it()
diff --git a/pkgs/test/lib/src/runner/browser/dom.dart b/pkgs/test/lib/src/runner/browser/dom.dart
index 98ed0e9..f71173f 100644
--- a/pkgs/test/lib/src/runner/browser/dom.dart
+++ b/pkgs/test/lib/src/runner/browser/dom.dart
@@ -134,7 +134,7 @@
   dynamic get data => js_util.dartify(js_util.getProperty(this, 'data'));
   external String get origin;
   List<MessagePort> get ports =>
-      js_util.getProperty<List>(this, 'ports').cast<MessagePort>();
+      js_util.getProperty<List<dynamic>>(this, 'ports').cast<MessagePort>();
 }
 
 @JS()
diff --git a/pkgs/test_api/CHANGELOG.md b/pkgs/test_api/CHANGELOG.md
index 1632870..0662f7c 100644
--- a/pkgs/test_api/CHANGELOG.md
+++ b/pkgs/test_api/CHANGELOG.md
@@ -1,3 +1,19 @@
+## 0.4.19-dev
+
+* Improve typing of a lot of internal APIs and a few public APIs:
+  * **Breaking Change**: `LiveTest.onComplete`, `LiveTest.run()`, and
+    `LiveTest.close()` each now return a `Future<void>` instead of a
+    `Future<dynamic>`. They never returned meaningful values, so it would be
+    very unusual to depend on using them.
+  * **Breaking Change**: `expectLater` now returns a `Future<void>` instead of
+    a `Future<dynamic>`. This future always contained `null`, so it would be
+    unusual to depend on using the value.
+  * **Breaking Change**: `RemoteListener.start`'s `beforeLoad` named parameter
+    must be a function which returns `Future<void>` (previously it needed to
+    return `Future<dynamic>`).
+  * **Breaking Change**: `errorsDontStopTest` now returns a `Future<void>`
+    instead of a `Future<dynamic>`.
+
 ## 0.4.18
 
 * Don't run `tearDown` until the test body and outstanding work is complete,
diff --git a/pkgs/test_api/lib/src/backend/declarer.dart b/pkgs/test_api/lib/src/backend/declarer.dart
index 7a37552..4ea6465 100644
--- a/pkgs/test_api/lib/src/backend/declarer.dart
+++ b/pkgs/test_api/lib/src/backend/declarer.dart
@@ -347,7 +347,7 @@
   ///
   /// If no set-up functions are declared, this returns a [Future] that
   /// completes immediately.
-  Future _runSetUps() async {
+  Future<void> _runSetUps() async {
     if (_parent != null) await _parent!._runSetUps();
     // TODO: why does type inference not work here?
     await Future.forEach<Function>(_setUps, (setUp) => setUp());
diff --git a/pkgs/test_api/lib/src/backend/live_test.dart b/pkgs/test_api/lib/src/backend/live_test.dart
index 683e87c..079a4d4 100644
--- a/pkgs/test_api/lib/src/backend/live_test.dart
+++ b/pkgs/test_api/lib/src/backend/live_test.dart
@@ -99,7 +99,7 @@
   /// first error or when all [expectAsync] callbacks have been called and any
   /// returned [Future] has completed, but it's possible for further processing
   /// to happen, which may cause further errors.
-  Future get onComplete;
+  Future<void> get onComplete;
 
   /// The name of this live test without any group prefixes.
   String get individualName {
@@ -125,7 +125,7 @@
   ///
   /// This returns the same [Future] as [onComplete]. It may not be called more
   /// than once.
-  Future run();
+  Future<void> run();
 
   /// Signals that this test should stop emitting events and release any
   /// resources it may have allocated.
@@ -144,5 +144,5 @@
   /// Returns a [Future] that completes once all resources are released *and*
   /// the test has completed. This allows the caller to wait until the test's
   /// tear-down logic has run.
-  Future close();
+  Future<void> close();
 }
diff --git a/pkgs/test_api/lib/src/backend/remote_listener.dart b/pkgs/test_api/lib/src/backend/remote_listener.dart
index 5aa0638..b07ae20 100644
--- a/pkgs/test_api/lib/src/backend/remote_listener.dart
+++ b/pkgs/test_api/lib/src/backend/remote_listener.dart
@@ -45,7 +45,7 @@
   /// for this worker.
   static StreamChannel<Object?> start(Function Function() getMain,
       {bool hidePrints = true,
-      Future Function(
+      Future<void> Function(
               StreamChannel<Object?> Function(String name) suiteChannel)?
           beforeLoad}) {
     // Synchronous in order to allow `print` output to show up immediately, even
@@ -86,7 +86,7 @@
         var message = await queue.next as Map;
         assert(message['type'] == 'initial');
 
-        queue.rest.cast<Map>().listen((message) {
+        queue.rest.cast<Map<Object?, Object?>>().listen((message) {
           if (message['type'] == 'close') {
             controller.local.sink.close();
             return;
@@ -144,7 +144,7 @@
 
   /// Returns a [Set] from a JSON serialized list of strings, or `null` if the
   /// list is empty or `null`.
-  static Set<String>? _deserializeSet(List? list) {
+  static Set<String>? _deserializeSet(List<Object?>? list) {
     if (list == null) return null;
     if (list.isEmpty) return null;
     return Set.from(list);
@@ -153,12 +153,13 @@
   /// Sends a message over [channel] indicating that the tests failed to load.
   ///
   /// [message] should describe the failure.
-  static void _sendLoadException(StreamChannel channel, String message) {
+  static void _sendLoadException(
+      StreamChannel<Object?> channel, String message) {
     channel.sink.add({'type': 'loadException', 'message': message});
   }
 
   /// Sends a message over [channel] indicating an error from user code.
-  static void _sendError(StreamChannel channel, Object error,
+  static void _sendError(StreamChannel<Object?> channel, Object error,
       StackTrace stackTrace, bool verboseChain) {
     channel.sink.add({
       'type': 'error',
@@ -173,7 +174,7 @@
 
   /// Send information about [_suite] across [channel] and start listening for
   /// commands to run the tests.
-  void _listen(MultiChannel channel) {
+  void _listen(MultiChannel<Object?> channel) {
     channel.sink.add({
       'type': 'success',
       'root': _serializeGroup(channel, _suite.group, [])
@@ -183,8 +184,8 @@
   /// Serializes [group] into a JSON-safe map.
   ///
   /// [parents] lists the groups that contain [group].
-  Map _serializeGroup(
-      MultiChannel channel, Group group, Iterable<Group> parents) {
+  Map<String, Object?> _serializeGroup(
+      MultiChannel<Object?> channel, Group group, Iterable<Group> parents) {
     parents = parents.toList()..add(group);
     return {
       'type': 'group',
@@ -210,12 +211,13 @@
   ///
   /// [groups] lists the groups that contain [test]. Returns `null` if [test]
   /// is `null`.
-  Map? _serializeTest(
-      MultiChannel channel, Test? test, Iterable<Group>? groups) {
+  Map<String, Object?>? _serializeTest(
+      MultiChannel<Object?> channel, Test? test, Iterable<Group>? groups) {
     if (test == null) return null;
 
     var testChannel = channel.virtualChannel();
     testChannel.stream.listen((message) {
+      message as Map<Object?, Object?>;
       assert(message['command'] == 'run');
       _runLiveTest(test.load(_suite, groups: groups),
           channel.virtualChannel((message['channel'] as num).toInt()));
@@ -236,8 +238,9 @@
   }
 
   /// Runs [liveTest] and sends the results across [channel].
-  void _runLiveTest(LiveTest liveTest, MultiChannel channel) {
+  void _runLiveTest(LiveTest liveTest, MultiChannel<Object?> channel) {
     channel.stream.listen((message) {
+      message as Map<Object?, Object?>;
       assert(message['command'] == 'close');
       liveTest.close();
     });
diff --git a/pkgs/test_api/lib/src/backend/util/pretty_print.dart b/pkgs/test_api/lib/src/backend/util/pretty_print.dart
index 5c011c5..6272d19 100644
--- a/pkgs/test_api/lib/src/backend/util/pretty_print.dart
+++ b/pkgs/test_api/lib/src/backend/util/pretty_print.dart
@@ -17,7 +17,7 @@
 /// This converts each element of [iter] to a string and separates them with
 /// commas and/or [conjunction] where appropriate. The [conjunction] defaults to
 /// "and".
-String toSentence(Iterable iter, {String conjunction = 'and'}) {
+String toSentence(Iterable<Object> iter, {String conjunction = 'and'}) {
   if (iter.length == 1) return iter.first.toString();
 
   var result = iter.take(iter.length - 1).join(', ');
diff --git a/pkgs/test_api/lib/src/expect/async_matcher.dart b/pkgs/test_api/lib/src/expect/async_matcher.dart
index a3cc965..287f24a 100644
--- a/pkgs/test_api/lib/src/expect/async_matcher.dart
+++ b/pkgs/test_api/lib/src/expect/async_matcher.dart
@@ -28,10 +28,12 @@
   dynamic /*FutureOr<String>*/ matchAsync(item);
 
   @override
-  bool matches(item, Map matchState) {
+  bool matches(item, Map<Object?, Object?> matchState) {
     final result = matchAsync(item);
-    expect(result,
-        anyOf([equals(null), TypeMatcher<Future>(), TypeMatcher<String>()]),
+    expect(
+        result,
+        anyOf(
+            [equals(null), TypeMatcher<Future<void>>(), TypeMatcher<String>()]),
         reason: 'matchAsync() may only return a String, a Future, or null.');
 
     if (result is Future) {
@@ -52,6 +54,6 @@
 
   @override
   Description describeMismatch(item, Description mismatchDescription,
-          Map matchState, bool verbose) =>
+          Map<Object?, Object?> matchState, bool verbose) =>
       StringDescription(matchState[this] as String);
 }
diff --git a/pkgs/test_api/lib/src/expect/expect.dart b/pkgs/test_api/lib/src/expect/expect.dart
index 3773000..9bdd963 100644
--- a/pkgs/test_api/lib/src/expect/expect.dart
+++ b/pkgs/test_api/lib/src/expect/expect.dart
@@ -12,7 +12,7 @@
 /// upon failures in [expect].
 @Deprecated('Will be removed in 0.13.0.')
 typedef ErrorFormatter = String Function(dynamic actual, Matcher matcher,
-    String? reason, Map matchState, bool verbose);
+    String? reason, Map<Object?, Object?> matchState, bool verbose);
 
 /// Assert that [actual] matches [matcher].
 ///
@@ -58,11 +58,11 @@
 ///
 /// If the matcher fails asynchronously, that failure is piped to the returned
 /// future where it can be handled by user code.
-Future expectLater(actual, matcher, {String? reason, skip}) =>
+Future<void> expectLater(actual, matcher, {String? reason, skip}) =>
     _expect(actual, matcher, reason: reason, skip: skip);
 
 /// The implementation of [expect] and [expectLater].
-Future _expect(actual, matcher,
+Future<void> _expect(actual, matcher,
     {String? reason, skip, bool verbose = false, ErrorFormatter? formatter}) {
   final test = TestHandle.current;
   formatter ??= (actual, matcher, reason, matchState, verbose) {
@@ -96,8 +96,10 @@
   if (matcher is AsyncMatcher) {
     // Avoid async/await so that expect() throws synchronously when possible.
     var result = matcher.matchAsync(actual);
-    expect(result,
-        anyOf([equals(null), TypeMatcher<Future>(), TypeMatcher<String>()]),
+    expect(
+        result,
+        anyOf(
+            [equals(null), TypeMatcher<Future<void>>(), TypeMatcher<String>()]),
         reason: 'matchAsync() may only return a String, a Future, or null.');
 
     if (result is String) {
diff --git a/pkgs/test_api/lib/src/expect/expect_async.dart b/pkgs/test_api/lib/src/expect/expect_async.dart
index dee7c1e..15592ed 100644
--- a/pkgs/test_api/lib/src/expect/expect_async.dart
+++ b/pkgs/test_api/lib/src/expect/expect_async.dart
@@ -178,7 +178,7 @@
       _run([a0, a1, a2, a3, a4, a5].where((a) => a != placeholder));
 
   /// Runs the wrapped function with [args] and returns its return value.
-  T _run(Iterable args) {
+  T _run(Iterable<Object?> args) {
     // Note that in the old test, this returned `null` if it encountered an
     // error, where now it just re-throws that error because Zone machinery will
     // pass it to the invoker anyway.
diff --git a/pkgs/test_api/lib/src/expect/future_matchers.dart b/pkgs/test_api/lib/src/expect/future_matchers.dart
index 9900c97..2e5aec1 100644
--- a/pkgs/test_api/lib/src/expect/future_matchers.dart
+++ b/pkgs/test_api/lib/src/expect/future_matchers.dart
@@ -96,7 +96,7 @@
   }
 
   @override
-  bool matches(item, Map matchState) {
+  bool matches(item, Map<dynamic, dynamic> matchState) {
     if (item is! Future) return false;
     item.then((value) {
       fail('Future was not expected to complete but completed with a value of '
@@ -107,8 +107,8 @@
   }
 
   @override
-  Description describeMismatch(
-      item, Description description, Map matchState, bool verbose) {
+  Description describeMismatch(item, Description description,
+      Map<dynamic, dynamic> matchState, bool verbose) {
     if (item is! Future) return description.add('$item is not a Future');
     return description;
   }
diff --git a/pkgs/test_api/lib/src/expect/stream_matcher.dart b/pkgs/test_api/lib/src/expect/stream_matcher.dart
index 6d7bddf..307a183 100644
--- a/pkgs/test_api/lib/src/expect/stream_matcher.dart
+++ b/pkgs/test_api/lib/src/expect/stream_matcher.dart
@@ -82,7 +82,8 @@
   /// The [description] should be in the subjunctive mood. This means that it
   /// should be grammatically valid when used after the word "should". For
   /// example, it might be "emit the right events".
-  factory StreamMatcher(Future<String?> Function(StreamQueue) matchQueue,
+  factory StreamMatcher(
+      Future<String?> Function(StreamQueue<dynamic>) matchQueue,
       String description) = _StreamMatcher;
 
   /// Tries to match events emitted by [queue].
@@ -101,7 +102,7 @@
   ///
   /// If the queue emits an error, that error is re-thrown unless otherwise
   /// indicated by the matcher.
-  Future<String?> matchQueue(StreamQueue queue);
+  Future<String?> matchQueue(StreamQueue<dynamic> queue);
 }
 
 /// A concrete implementation of [StreamMatcher].
@@ -113,16 +114,16 @@
   final String description;
 
   /// The callback used to implement [matchQueue].
-  final Future<String?> Function(StreamQueue) _matchQueue;
+  final Future<String?> Function(StreamQueue<dynamic>) _matchQueue;
 
   _StreamMatcher(this._matchQueue, this.description);
 
   @override
-  Future<String?> matchQueue(StreamQueue queue) => _matchQueue(queue);
+  Future<String?> matchQueue(StreamQueue<dynamic> queue) => _matchQueue(queue);
 
   @override
   dynamic /*FutureOr<String>*/ matchAsync(item) {
-    StreamQueue queue;
+    StreamQueue<dynamic> queue;
     var shouldCancelQueue = false;
     if (item is StreamQueue) {
       queue = item;
@@ -148,7 +149,7 @@
       // Get a list of events emitted by the stream so we can emit them as part
       // of the error message.
       var replay = transaction.newQueue();
-      var events = <Result?>[];
+      var events = <Result<Object>?>[];
       var subscription = Result.captureStreamTransformer
           .bind(replay.rest.cast())
           .listen(events.add, onDone: () => events.add(null));
diff --git a/pkgs/test_api/lib/src/expect/stream_matchers.dart b/pkgs/test_api/lib/src/expect/stream_matchers.dart
index df2fbbd..2cf5e4d 100644
--- a/pkgs/test_api/lib/src/expect/stream_matchers.dart
+++ b/pkgs/test_api/lib/src/expect/stream_matchers.dart
@@ -82,7 +82,7 @@
 /// If any matchers match the stream, no errors from other matchers are thrown.
 /// If no matchers match and multiple matchers threw errors, the first error is
 /// re-thrown.
-StreamMatcher emitsAnyOf(Iterable matchers) {
+StreamMatcher emitsAnyOf(Iterable<dynamic> matchers) {
   var streamMatchers = matchers.map(emits).toList();
   if (streamMatchers.isEmpty) {
     throw ArgumentError('matcher may not be empty');
@@ -105,8 +105,8 @@
     Object? firstError;
     StackTrace? firstStackTrace;
 
-    var futures = <Future>[];
-    StreamQueue? consumedMost;
+    var futures = <Future<void>>[];
+    StreamQueue<dynamic>? consumedMost;
     for (var i = 0; i < matchers.length; i++) {
       futures.add(() async {
         var copy = transaction.newQueue();
@@ -162,7 +162,7 @@
 /// [matchers] matches, one after another.
 ///
 /// If any matcher fails to match, this fails and consumes no events.
-StreamMatcher emitsInOrder(Iterable matchers) {
+StreamMatcher emitsInOrder(Iterable<dynamic> matchers) {
   var streamMatchers = matchers.map(emits).toList();
   if (streamMatchers.length == 1) return streamMatchers.first;
 
@@ -284,7 +284,7 @@
 /// Returns whether [matcher] matches [queue] at its current position.
 ///
 /// This treats errors as failures to match.
-Future<bool> _tryMatch(StreamQueue queue, StreamMatcher matcher) {
+Future<bool> _tryMatch(StreamQueue<dynamic> queue, StreamMatcher matcher) {
   return queue.withTransaction((copy) async {
     try {
       return (await matcher.matchQueue(copy)) == null;
@@ -307,7 +307,7 @@
 ///
 /// Note that checking every ordering of [matchers] is O(n!) in the worst case,
 /// so this should only be called when there are very few [matchers].
-StreamMatcher emitsInAnyOrder(Iterable matchers) {
+StreamMatcher emitsInAnyOrder(Iterable<dynamic> matchers) {
   var streamMatchers = matchers.map(emits).toSet();
   if (streamMatchers.length == 1) return streamMatchers.first;
   var description = 'do the following in any order:\n'
@@ -320,13 +320,13 @@
 
 /// Returns whether [queue] matches [matchers] in any order.
 Future<bool> _tryInAnyOrder(
-    StreamQueue queue, Set<StreamMatcher> matchers) async {
+    StreamQueue<dynamic> queue, Set<StreamMatcher> matchers) async {
   if (matchers.length == 1) {
     return await matchers.first.matchQueue(queue) == null;
   }
 
   var transaction = queue.startTransaction();
-  StreamQueue? consumedMost;
+  StreamQueue<dynamic>? consumedMost;
 
   // The first error thrown. If no matchers match and this exists, we rethrow
   // it.
diff --git a/pkgs/test_api/lib/src/scaffolding/spawn_hybrid.dart b/pkgs/test_api/lib/src/scaffolding/spawn_hybrid.dart
index 6e01fe8..344e349 100644
--- a/pkgs/test_api/lib/src/scaffolding/spawn_hybrid.dart
+++ b/pkgs/test_api/lib/src/scaffolding/spawn_hybrid.dart
@@ -89,7 +89,8 @@
 ///
 /// **Note**: If you use this API, be sure to add a dependency on the
 /// **`stream_channel` package, since you're using its API as well!
-StreamChannel spawnHybridUri(uri, {Object? message, bool stayAlive = false}) {
+StreamChannel<dynamic> spawnHybridUri(uri,
+    {Object? message, bool stayAlive = false}) {
   if (uri is String) {
     // Ensure that it can be parsed as a uri.
     Uri.parse(uri);
@@ -139,7 +140,7 @@
 ///
 /// **Note**: If you use this API, be sure to add a dependency on the
 /// **`stream_channel` package, since you're using its API as well!
-StreamChannel spawnHybridCode(String dartCode,
+StreamChannel<dynamic> spawnHybridCode(String dartCode,
     {Object? message, bool stayAlive = false}) {
   var uri = Uri.dataFromString(dartCode,
       encoding: utf8, mimeType: 'application/dart');
@@ -147,7 +148,8 @@
 }
 
 /// Like [spawnHybridUri], but doesn't take [Uri] objects.
-StreamChannel _spawn(String uri, Object? message, {bool stayAlive = false}) {
+StreamChannel<dynamic> _spawn(String uri, Object? message,
+    {bool stayAlive = false}) {
   var channel = Zone.current[#test.runner.test_channel] as MultiChannel?;
   if (channel == null) {
     throw UnsupportedError("Can't connect to the test runner.\n"
@@ -157,7 +159,7 @@
   ensureJsonEncodable(message);
 
   var virtualChannel = channel.virtualChannel();
-  StreamChannel isolateChannel = virtualChannel;
+  StreamChannel<dynamic> isolateChannel = virtualChannel;
   channel.sink.add({
     'type': 'spawn-hybrid-uri',
     'url': uri,
@@ -166,7 +168,7 @@
   });
 
   if (!stayAlive) {
-    var disconnector = Disconnector();
+    var disconnector = Disconnector<dynamic>();
     addTearDown(() => disconnector.disconnect());
     isolateChannel = isolateChannel.transform(disconnector);
   }
diff --git a/pkgs/test_api/lib/src/scaffolding/utils.dart b/pkgs/test_api/lib/src/scaffolding/utils.dart
index 134dc49..5a62a80 100644
--- a/pkgs/test_api/lib/src/scaffolding/utils.dart
+++ b/pkgs/test_api/lib/src/scaffolding/utils.dart
@@ -13,7 +13,7 @@
 ///
 /// Awaiting this approximates waiting until all asynchronous work (other than
 /// work that's waiting for external resources) completes.
-Future pumpEventQueue({int times = 20}) {
+Future<void> pumpEventQueue({int times = 20}) {
   if (times == 0) return Future.value();
   // Use the event loop to allow the microtask queue to finish.
   return Future(() => pumpEventQueue(times: times - 1));
diff --git a/pkgs/test_api/pubspec.yaml b/pkgs/test_api/pubspec.yaml
index 5752c69..116f9b8 100644
--- a/pkgs/test_api/pubspec.yaml
+++ b/pkgs/test_api/pubspec.yaml
@@ -1,5 +1,5 @@
 name: test_api
-version: 0.4.18
+version: 0.4.19-dev
 description: >-
   The user facing API for structuring Dart tests and checking expectations.
 repository: https://github.com/dart-lang/test/tree/master/pkgs/test_api
diff --git a/pkgs/test_api/test/backend/declarer_test.dart b/pkgs/test_api/test/backend/declarer_test.dart
index 0fe848a..b092980 100644
--- a/pkgs/test_api/test/backend/declarer_test.dart
+++ b/pkgs/test_api/test/backend/declarer_test.dart
@@ -690,7 +690,7 @@
 ///
 /// This automatically sets up an `onError` listener to ensure that the test
 /// doesn't throw any invisible exceptions.
-Future _runTest(Test test, {bool shouldFail = false}) {
+Future<void> _runTest(Test test, {bool shouldFail = false}) {
   var liveTest = test.load(_suite);
 
   if (shouldFail) {
diff --git a/pkgs/test_api/test/frontend/expect_async_test.dart b/pkgs/test_api/test/frontend/expect_async_test.dart
index dde2e41..d8a5b40 100644
--- a/pkgs/test_api/test/frontend/expect_async_test.dart
+++ b/pkgs/test_api/test/frontend/expect_async_test.dart
@@ -170,7 +170,7 @@
         "won't allow the test to complete until it's called at least that "
         'many times', () async {
       late LiveTest liveTest;
-      late Future future;
+      late Future<void> future;
       liveTest = createTest(() {
         var callback = expectAsync0(() {}, count: 3);
 
@@ -269,7 +269,7 @@
     test("won't allow the test to complete until isDone returns true",
         () async {
       late LiveTest liveTest;
-      late Future future;
+      late Future<void> future;
       liveTest = createTest(() {
         var done = false;
         var callback = expectAsyncUntil0(() {}, () => done);
diff --git a/pkgs/test_api/test/frontend/stream_matcher_test.dart b/pkgs/test_api/test/frontend/stream_matcher_test.dart
index deb21fb..38a5895 100644
--- a/pkgs/test_api/test/frontend/stream_matcher_test.dart
+++ b/pkgs/test_api/test/frontend/stream_matcher_test.dart
@@ -15,10 +15,10 @@
     glyph.ascii = true;
   });
 
-  late Stream stream;
-  late StreamQueue queue;
-  late Stream errorStream;
-  late StreamQueue errorQueue;
+  late Stream<int> stream;
+  late StreamQueue<int> queue;
+  late Stream<void> errorStream;
+  late StreamQueue<void> errorQueue;
   setUp(() {
     stream = Stream.fromIterable([1, 2, 3, 4, 5]);
     queue = StreamQueue(Stream.fromIterable([1, 2, 3, 4, 5]));
diff --git a/pkgs/test_api/test/utils.dart b/pkgs/test_api/test/utils.dart
index 65dd5c5..33ad0de 100644
--- a/pkgs/test_api/test/utils.dart
+++ b/pkgs/test_api/test/utils.dart
@@ -134,10 +134,10 @@
 /// is called at some later time.
 ///
 /// [stopBlocking] is passed the return value of [test].
-Future expectTestBlocks(
+Future<void> expectTestBlocks(
     dynamic Function() test, dynamic Function(dynamic) stopBlocking) async {
   late LiveTest liveTest;
-  late Future future;
+  late Future<void> future;
   liveTest = createTest(() {
     var value = test();
     future = pumpEventQueue().then((_) {
@@ -158,7 +158,7 @@
 ///
 /// This is typically used to run multiple tests where later tests make
 /// assertions about the results of previous ones.
-Future expectTestsPass(void Function() body) async {
+Future<void> expectTestsPass(void Function() body) async {
   var engine = declareEngine(body);
   var success = await engine.run();
 
diff --git a/pkgs/test_core/lib/src/executable.dart b/pkgs/test_core/lib/src/executable.dart
index 0c7530b..b9d4559 100644
--- a/pkgs/test_core/lib/src/executable.dart
+++ b/pkgs/test_core/lib/src/executable.dart
@@ -20,7 +20,7 @@
 import 'util/exit_codes.dart' as exit_codes;
 import 'util/io.dart';
 
-StreamSubscription? signalSubscription;
+StreamSubscription<ProcessSignal>? signalSubscription;
 bool isShutdown = false;
 
 /// Returns the path to the global test configuration file.
@@ -63,7 +63,7 @@
   final signals = Platform.isWindows
       ? ProcessSignal.sigint.watch()
       : Platform.isFuchsia // Signals don't exist on Fuchsia.
-          ? Stream.empty()
+          ? Stream<ProcessSignal>.empty()
           : StreamGroup.merge(
               [ProcessSignal.sigterm.watch(), ProcessSignal.sigint.watch()]);
 
diff --git a/pkgs/test_core/lib/src/runner.dart b/pkgs/test_core/lib/src/runner.dart
index ae7d6ee..80e0a75 100644
--- a/pkgs/test_core/lib/src/runner.dart
+++ b/pkgs/test_core/lib/src/runner.dart
@@ -53,7 +53,7 @@
   final Reporter _reporter;
 
   /// The subscription to the stream returned by [_loadSuites].
-  StreamSubscription? _suiteSubscription;
+  StreamSubscription<LoadSuite>? _suiteSubscription;
 
   /// The set of suite paths for which [_warnForUnknownTags] has already been
   /// called.
@@ -65,7 +65,7 @@
   /// The current debug operation, if any.
   ///
   /// This is stored so that we can cancel it when the runner is closed.
-  CancelableOperation? _debugOperation;
+  CancelableOperation<void>? _debugOperation;
 
   /// The memoizer for ensuring [close] only runs once.
   final _closeMemo = AsyncMemoizer();
@@ -127,7 +127,7 @@
         } else {
           var subscription =
               _suiteSubscription = suites.listen(_engine.suiteSink.add);
-          var results = await Future.wait(<Future>[
+          var results = await Future.wait([
             subscription.asFuture().then((_) => _engine.suiteSink.close()),
             _engine.run()
           ], eagerError: true);
@@ -174,7 +174,7 @@
     if (unsupportedRuntimes.isEmpty) return;
 
     // Human-readable names for all unsupported runtimes.
-    var unsupportedNames = [];
+    var unsupportedNames = <Object>[];
 
     // If the user tried to run on one or moe unsupported browsers, figure out
     // whether we should warn about the individual browsers or whether all
@@ -216,7 +216,7 @@
   /// This stops any future test suites from running. It will wait for any
   /// currently-running VM tests, in case they have stuff to clean up on the
   /// filesystem.
-  Future close() => _closeMemo.runOnce(() async {
+  Future<void> close() => _closeMemo.runOnce(() async {
         Timer? timer;
         if (!_engine.isIdle) {
           // Wait a bit to print this message, since printing it eagerly looks weird
@@ -445,9 +445,10 @@
     var subscription = _suiteSubscription = suites.asyncMap((loadSuite) async {
       var operation = _debugOperation = debug(_engine, _reporter, loadSuite);
       await operation.valueOrCancellation();
+      return loadSuite;
     }).listen(null);
 
-    var results = await Future.wait(<Future>[
+    var results = await Future.wait([
       subscription.asFuture().then((_) => _engine.suiteSink.close()),
       _engine.run()
     ], eagerError: true);
diff --git a/pkgs/test_core/lib/src/runner/console.dart b/pkgs/test_core/lib/src/runner/console.dart
index b716aad..56c48ef 100644
--- a/pkgs/test_core/lib/src/runner/console.dart
+++ b/pkgs/test_core/lib/src/runner/console.dart
@@ -16,7 +16,7 @@
   final _commands = <String, _Command>{};
 
   /// The pending next line of standard input, if we're waiting on one.
-  CancelableOperation? _nextLine;
+  CancelableOperation<String>? _nextLine;
 
   /// Whether the console is currently running.
   bool _running = false;
diff --git a/pkgs/test_core/lib/src/runner/dart2js_compiler_pool.dart b/pkgs/test_core/lib/src/runner/dart2js_compiler_pool.dart
index c1bd159..73cc279 100644
--- a/pkgs/test_core/lib/src/runner/dart2js_compiler_pool.dart
+++ b/pkgs/test_core/lib/src/runner/dart2js_compiler_pool.dart
@@ -38,7 +38,7 @@
   /// The returned [Future] will complete once the `dart2js` process completes
   /// *and* all its output has been printed to the command line.
   @override
-  Future compileInternal(
+  Future<void> compileInternal(
       String code, String path, SuiteConfiguration suiteConfig) {
     return withTempDir((dir) async {
       var wrapperPath = p.join(dir, 'runInBrowser.dart');
diff --git a/pkgs/test_core/lib/src/runner/debugger.dart b/pkgs/test_core/lib/src/runner/debugger.dart
index a24bd1f..c532f61 100644
--- a/pkgs/test_core/lib/src/runner/debugger.dart
+++ b/pkgs/test_core/lib/src/runner/debugger.dart
@@ -24,7 +24,7 @@
 /// Returns a [CancelableOperation] that will complete once the suite has
 /// finished running. If the operation is canceled, the debugger will clean up
 /// any resources it allocated.
-CancelableOperation debug(
+CancelableOperation<void> debug(
     Engine engine, Reporter reporter, LoadSuite loadSuite) {
   _Debugger? debugger;
   var canceled = false;
@@ -76,7 +76,7 @@
   StreamSubscription<bool>? _onDebuggingSubscription;
 
   /// The subscription to [_suite.environment.onRestart].
-  late final StreamSubscription _onRestartSubscription;
+  late final StreamSubscription<void> _onRestartSubscription;
 
   /// Whether [close] has been called.
   bool _closed = false;
@@ -97,7 +97,7 @@
   ///
   /// This prints information about the suite's debugger, then once the user has
   /// had a chance to set breakpoints, runs the suite's tests.
-  Future run() async {
+  Future<void> run() async {
     try {
       await _pause();
       if (_closed) return;
@@ -119,7 +119,7 @@
 
   /// Prints URLs for the [_suite]'s debugger and waits for the user to tell the
   /// suite to run.
-  Future _pause() async {
+  Future<void> _pause() async {
     if (!_suite.environment.supportsDebugging) return;
 
     try {
diff --git a/pkgs/test_core/lib/src/runner/engine.dart b/pkgs/test_core/lib/src/runner/engine.dart
index 578a6bf..35bed61 100644
--- a/pkgs/test_core/lib/src/runner/engine.dart
+++ b/pkgs/test_core/lib/src/runner/engine.dart
@@ -75,12 +75,12 @@
   /// A completer that will complete when this engine is unpaused.
   ///
   /// `null` if this engine is not paused.
-  Completer? _pauseCompleter;
+  Completer<void>? _pauseCompleter;
 
   /// A future that completes once this is unpaused.
   ///
   /// If this engine isn't paused, this future completes immediately.
-  Future get _onUnpaused =>
+  Future<void> get _onUnpaused =>
       _pauseCompleter == null ? Future.value() : _pauseCompleter!.future;
 
   /// Whether all tests passed or were skipped.
@@ -89,7 +89,7 @@
   /// This will be `null` if [close] was called before all the tests finished
   /// running.
   Future<bool?> get success async {
-    await Future.wait(<Future>[_group.future, _runPool.done], eagerError: true);
+    await Future.wait([_group.future, _runPool.done], eagerError: true);
     if (_closedBeforeDone!) return null;
     return liveTests.every((liveTest) =>
         liveTest.state.result.isPassing &&
@@ -100,7 +100,7 @@
   final _group = FutureGroup();
 
   /// All of the engine's stream subscriptions.
-  final _subscriptions = <StreamSubscription>{};
+  final _subscriptions = <StreamSubscription<void>>{};
 
   /// A sink used to pass [RunnerSuite]s in to the engine to run.
   ///
@@ -189,7 +189,7 @@
 
   /// A broadcast stream that fires an event whenever [isIdle] switches from
   /// `false` to `true`.
-  Stream get onIdle => _group.onIdle;
+  Stream<void> get onIdle => _group.onIdle;
 
   /// Creates an [Engine] that will run all tests provided via [suiteSink].
   ///
@@ -290,7 +290,7 @@
   /// [parents] is a list of groups that contain [group]. It may be modified,
   /// but it's guaranteed to be in its original state once this function has
   /// finished.
-  Future _runGroup(LiveSuiteController suiteController, Group group,
+  Future<void> _runGroup(LiveSuiteController suiteController, Group group,
       List<Group> parents) async {
     parents.add(group);
     try {
@@ -345,7 +345,8 @@
   ///
   /// If [countSuccess] is `true` (the default), the test is put into [passed]
   /// if it succeeds. Otherwise, it's removed from [liveTests] entirely.
-  Future _runLiveTest(LiveSuiteController suiteController, LiveTest liveTest,
+  Future<void> _runLiveTest(
+      LiveSuiteController suiteController, LiveTest liveTest,
       {bool countSuccess = true}) async {
     await _onUnpaused;
     _active.add(liveTest);
@@ -381,7 +382,7 @@
   ///
   /// [suiteController] is the controller for the suite that contains [test].
   /// [parents] is a list of groups that contain [test].
-  Future _runSkippedTest(LiveSuiteController suiteController, Test test,
+  Future<void> _runSkippedTest(LiveSuiteController suiteController, Test test,
       List<Group> parents) async {
     await _onUnpaused;
     var skipped = LocalTest(test.name, test.metadata, () {}, trace: test.trace);
@@ -408,7 +409,7 @@
   /// running.
   ///
   /// Returns the same future as [LiveTest.close].
-  Future restartTest(LiveTest liveTest) async {
+  Future<void> restartTest(LiveTest liveTest) async {
     if (_activeSuiteLoads.contains(liveTest)) {
       throw ArgumentError("Can't restart a load test.");
     }
@@ -512,7 +513,7 @@
   /// **Note that closing the engine is not the same as closing [suiteSink].**
   /// Closing [suiteSink] indicates that no more input will be provided, closing
   /// the engine indicates that no more output should be emitted.
-  Future close() async {
+  Future<void> close() async {
     _closed = true;
     if (_closedBeforeDone != null) _closedBeforeDone = true;
     await _suiteController.close();
diff --git a/pkgs/test_core/lib/src/runner/environment.dart b/pkgs/test_core/lib/src/runner/environment.dart
index 6b28faa..671ee66 100644
--- a/pkgs/test_core/lib/src/runner/environment.dart
+++ b/pkgs/test_core/lib/src/runner/environment.dart
@@ -24,14 +24,14 @@
   /// environment to restart the current test once it's finished.
   ///
   /// Never emits an error, and never closes.
-  Stream get onRestart;
+  Stream<void> get onRestart;
 
   /// Displays information indicating that the test runner is paused.
   ///
   /// The returned operation will complete when the user takes action within the
   /// environment that should unpause the runner. If the runner is unpaused
   /// elsewhere, the operation should be canceled.
-  CancelableOperation displayPause();
+  CancelableOperation<void> displayPause();
 }
 
 /// The default environment for platform plugins.
@@ -39,7 +39,7 @@
   @override
   final supportsDebugging = false;
   @override
-  Stream get onRestart => StreamController.broadcast().stream;
+  Stream<void> get onRestart => StreamController.broadcast().stream;
 
   const PluginEnvironment();
 
@@ -50,6 +50,6 @@
   Uri? get remoteDebuggerUrl => null;
 
   @override
-  CancelableOperation displayPause() => throw UnsupportedError(
+  CancelableOperation<void> displayPause() => throw UnsupportedError(
       'PluginEnvironment.displayPause is not supported.');
 }
diff --git a/pkgs/test_core/lib/src/runner/hybrid_listener.dart b/pkgs/test_core/lib/src/runner/hybrid_listener.dart
index f7fdb86..b570935 100644
--- a/pkgs/test_core/lib/src/runner/hybrid_listener.dart
+++ b/pkgs/test_core/lib/src/runner/hybrid_listener.dart
@@ -33,7 +33,7 @@
 ///
 /// The [data] argument contains two values: a [SendPort] that communicates with
 /// the main isolate, and a message to pass to `hybridMain()`.
-void listen(Function Function() getMain, List data) {
+void listen(Function Function() getMain, List<Object?> data) {
   var channel = IsolateChannel.connectSend(data.first as SendPort);
   var message = data.last;
 
@@ -53,8 +53,8 @@
       if (main is! Function) {
         _sendError(channel, 'Top-level hybridMain is not a function.');
         return;
-      } else if (main is! Function(StreamChannel) &&
-          main is! Function(StreamChannel, Never)) {
+      } else if (main is! Function(StreamChannel<Object?>) &&
+          main is! Function(StreamChannel<Object?>, Never)) {
         if (main is Function(StreamChannel<Never>) ||
             main is Function(StreamChannel<Never>, Never)) {
           _sendError(
@@ -73,7 +73,7 @@
       // errors and distinguish user data events from control events sent by the
       // listener.
       var transformedChannel = channel.transformSink(_transformer);
-      if (main is Function(StreamChannel)) {
+      if (main is Function(StreamChannel<Object?>)) {
         main(transformedChannel);
       } else {
         main(transformedChannel, message);
@@ -89,7 +89,8 @@
 }
 
 /// Sends a message over [channel] indicating an error from user code.
-void _sendError(StreamChannel channel, error, [StackTrace? stackTrace]) {
+void _sendError(StreamChannel<Object?> channel, error,
+    [StackTrace? stackTrace]) {
   channel.sink.add({
     'type': 'error',
     'error': RemoteException.serialize(error, stackTrace ?? Chain.current())
diff --git a/pkgs/test_core/lib/src/runner/live_suite.dart b/pkgs/test_core/lib/src/runner/live_suite.dart
index 771655d..f101178 100644
--- a/pkgs/test_core/lib/src/runner/live_suite.dart
+++ b/pkgs/test_core/lib/src/runner/live_suite.dart
@@ -26,7 +26,7 @@
   ///
   /// The [onClose] future can be used to determine when the suite and its tests
   /// are guaranteed to emit no more events.
-  Future get onComplete;
+  Future<void> get onComplete;
 
   /// Whether the suite has been closed.
   ///
@@ -40,7 +40,7 @@
   /// Once this completes, no code is running for the suite or any of its tests.
   /// At this point, the caller can be sure that the suites' tests are all in
   /// fixed states that will not change in the future.
-  Future get onClose;
+  Future<void> get onClose;
 
   /// All the currently-known tests in this suite that have run or are running.
   ///
diff --git a/pkgs/test_core/lib/src/runner/live_suite_controller.dart b/pkgs/test_core/lib/src/runner/live_suite_controller.dart
index bc13e51..9c3541d 100644
--- a/pkgs/test_core/lib/src/runner/live_suite_controller.dart
+++ b/pkgs/test_core/lib/src/runner/live_suite_controller.dart
@@ -21,13 +21,13 @@
   RunnerSuite get suite => _controller._suite;
 
   @override
-  Future get onComplete => _controller._onCompleteGroup.future;
+  Future<void> get onComplete => _controller._onCompleteGroup.future;
 
   @override
   bool get isClosed => _controller._onCloseCompleter.isCompleted;
 
   @override
-  Future get onClose => _controller._onCloseCompleter.future;
+  Future<void> get onClose => _controller._onCloseCompleter.future;
 
   @override
   Stream<LiveTest> get onTestStarted =>
@@ -143,7 +143,7 @@
   }
 
   /// Closes the underlying suite.
-  Future close() => _closeMemo.runOnce(() async {
+  Future<void> close() => _closeMemo.runOnce(() async {
         try {
           await _suite.close();
         } finally {
diff --git a/pkgs/test_core/lib/src/runner/load_suite.dart b/pkgs/test_core/lib/src/runner/load_suite.dart
index 2c64fc0..d5ee2ff 100644
--- a/pkgs/test_core/lib/src/runner/load_suite.dart
+++ b/pkgs/test_core/lib/src/runner/load_suite.dart
@@ -214,7 +214,7 @@
   }
 
   @override
-  Future close() async {}
+  Future<void> close() async {}
 
   @override
   Future<Map<String, dynamic>> gatherCoverage() =>
diff --git a/pkgs/test_core/lib/src/runner/loader.dart b/pkgs/test_core/lib/src/runner/loader.dart
index 6cd47d2..e4e3344 100644
--- a/pkgs/test_core/lib/src/runner/loader.dart
+++ b/pkgs/test_core/lib/src/runner/loader.dart
@@ -272,7 +272,7 @@
     }
   }
 
-  Future closeEphemeral() async {
+  Future<void> closeEphemeral() async {
     await Future.wait(_platformPlugins.values.map((memo) async {
       if (!memo.hasRun) return;
       await (await memo.future).closeEphemeral();
@@ -280,7 +280,7 @@
   }
 
   /// Closes the loader and releases all resources allocated by it.
-  Future close() => _closeMemo.runOnce(() async {
+  Future<void> close() => _closeMemo.runOnce(() async {
         await Future.wait([
           Future.wait(_platformPlugins.values.map((memo) async {
             if (!memo.hasRun) return;
diff --git a/pkgs/test_core/lib/src/runner/platform.dart b/pkgs/test_core/lib/src/runner/platform.dart
index 3048052..a03395c 100644
--- a/pkgs/test_core/lib/src/runner/platform.dart
+++ b/pkgs/test_core/lib/src/runner/platform.dart
@@ -37,7 +37,7 @@
   Future<RunnerSuite?> load(String path, SuitePlatform platform,
       SuiteConfiguration suiteConfig, Map<String, Object?> message);
 
-  Future closeEphemeral() async {}
+  Future<void> closeEphemeral() async {}
 
-  Future close() async {}
+  Future<void> close() async {}
 }
diff --git a/pkgs/test_core/lib/src/runner/plugin/environment.dart b/pkgs/test_core/lib/src/runner/plugin/environment.dart
index a097842..227b4d9 100644
--- a/pkgs/test_core/lib/src/runner/plugin/environment.dart
+++ b/pkgs/test_core/lib/src/runner/plugin/environment.dart
@@ -13,7 +13,7 @@
   @override
   final supportsDebugging = false;
   @override
-  Stream get onRestart => StreamController.broadcast().stream;
+  Stream<void> get onRestart => StreamController.broadcast().stream;
 
   const PluginEnvironment();
 
@@ -24,6 +24,6 @@
   Uri? get remoteDebuggerUrl => null;
 
   @override
-  CancelableOperation displayPause() => throw UnsupportedError(
+  CancelableOperation<void> displayPause() => throw UnsupportedError(
       'PluginEnvironment.displayPause is not supported.');
 }
diff --git a/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart b/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
index f40a71c..52fb9db 100644
--- a/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
+++ b/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
@@ -102,8 +102,8 @@
 
           case 'success':
             var deserializer = _Deserializer(suiteChannel);
-            completer.complete(
-                deserializer.deserializeGroup(response['root'] as Map));
+            completer.complete(deserializer
+                .deserializeGroup(response['root'] as Map<String, Object?>));
             break;
         }
       },
@@ -125,17 +125,17 @@
 /// A utility class for storing state while deserializing tests.
 class _Deserializer {
   /// The channel over which tests communicate.
-  final MultiChannel _channel;
+  final MultiChannel<Object?> _channel;
 
   _Deserializer(this._channel);
 
   /// Deserializes [group] into a concrete [Group].
-  Group deserializeGroup(Map group) {
+  Group deserializeGroup(Map<String, Object?> group) {
     var metadata = Metadata.deserialize(group['metadata']);
     return Group(
         group['name'] as String,
         (group['entries'] as List).map((entry) {
-          var map = entry as Map;
+          var map = entry as Map<String, Object?>;
           if (map['type'] == 'group') return deserializeGroup(map);
           return _deserializeTest(map)!;
         }),
@@ -143,14 +143,15 @@
         trace: group['trace'] == null
             ? null
             : Trace.parse(group['trace'] as String),
-        setUpAll: _deserializeTest(group['setUpAll'] as Map?),
-        tearDownAll: _deserializeTest(group['tearDownAll'] as Map?));
+        setUpAll: _deserializeTest(group['setUpAll'] as Map<String, Object?>?),
+        tearDownAll:
+            _deserializeTest(group['tearDownAll'] as Map<String, Object?>?));
   }
 
   /// Deserializes [test] into a concrete [Test] class.
   ///
   /// Returns `null` if [test] is `null`.
-  Test? _deserializeTest(Map? test) {
+  Test? _deserializeTest(Map<String, Object?>? test) {
     if (test == null) return null;
 
     var metadata = Metadata.deserialize(test['metadata']);
diff --git a/pkgs/test_core/lib/src/runner/plugin/remote_platform_helpers.dart b/pkgs/test_core/lib/src/runner/plugin/remote_platform_helpers.dart
index 7e50207..eb6608a 100644
--- a/pkgs/test_core/lib/src/runner/plugin/remote_platform_helpers.dart
+++ b/pkgs/test_core/lib/src/runner/plugin/remote_platform_helpers.dart
@@ -27,7 +27,7 @@
 /// for this worker.
 StreamChannel<Object?> serializeSuite(Function Function() getMain,
         {bool hidePrints = true,
-        Future Function(
+        Future<void> Function(
                 StreamChannel<Object?> Function(String name) suiteChannel)?
             beforeLoad}) =>
     RemoteListener.start(
diff --git a/pkgs/test_core/lib/src/runner/reporter/compact.dart b/pkgs/test_core/lib/src/runner/reporter/compact.dart
index 6760a91..e8e37d9 100644
--- a/pkgs/test_core/lib/src/runner/reporter/compact.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/compact.dart
@@ -107,7 +107,7 @@
   var _shouldPrintStackTraceChainingNotice = false;
 
   /// The set of all subscriptions to various streams.
-  final _subscriptions = <StreamSubscription>{};
+  final _subscriptions = <StreamSubscription<void>>{};
 
   final StringSink _sink;
 
diff --git a/pkgs/test_core/lib/src/runner/reporter/expanded.dart b/pkgs/test_core/lib/src/runner/reporter/expanded.dart
index 6ff2b6f..4b9cdb5 100644
--- a/pkgs/test_core/lib/src/runner/reporter/expanded.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/expanded.dart
@@ -86,7 +86,7 @@
   var _shouldPrintStackTraceChainingNotice = false;
 
   /// The set of all subscriptions to various streams.
-  final _subscriptions = <StreamSubscription>{};
+  final _subscriptions = <StreamSubscription<void>>{};
 
   final StringSink _sink;
 
diff --git a/pkgs/test_core/lib/src/runner/reporter/github.dart b/pkgs/test_core/lib/src/runner/reporter/github.dart
index ed9ab5a..2c291d6 100644
--- a/pkgs/test_core/lib/src/runner/reporter/github.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/github.dart
@@ -36,7 +36,7 @@
   var _paused = false;
 
   /// The set of all subscriptions to various streams.
-  final _subscriptions = <StreamSubscription>{};
+  final _subscriptions = <StreamSubscription<void>>{};
 
   final StringSink _sink;
 
diff --git a/pkgs/test_core/lib/src/runner/reporter/json.dart b/pkgs/test_core/lib/src/runner/reporter/json.dart
index 5497817..fca91ea 100644
--- a/pkgs/test_core/lib/src/runner/reporter/json.dart
+++ b/pkgs/test_core/lib/src/runner/reporter/json.dart
@@ -59,7 +59,7 @@
   var _paused = false;
 
   /// The set of all subscriptions to various streams.
-  final _subscriptions = <StreamSubscription>{};
+  final _subscriptions = <StreamSubscription<void>>{};
 
   final StringSink _sink;
 
@@ -237,7 +237,8 @@
   }
 
   /// Serializes [metadata] into a JSON-protocol-compatible map.
-  Map _serializeMetadata(SuiteConfiguration suiteConfig, Metadata metadata) =>
+  Map<String, Object?> _serializeMetadata(
+          SuiteConfiguration suiteConfig, Metadata metadata) =>
       suiteConfig.runSkipped
           ? {'skip': false, 'skipReason': null}
           : {'skip': metadata.skip, 'skipReason': metadata.skipReason};
@@ -289,7 +290,7 @@
       suite is RunnerSuite ? suite.config : SuiteConfiguration.empty;
 
   /// Emits an event with the given type and attributes.
-  void _emit(String type, Map attributes) {
+  void _emit(String type, Map<String, Object?> attributes) {
     attributes['type'] = type;
     attributes['time'] = _stopwatch.elapsed.inMilliseconds;
     _sink.writeln(jsonEncode(attributes));
diff --git a/pkgs/test_core/lib/src/runner/runner_suite.dart b/pkgs/test_core/lib/src/runner/runner_suite.dart
index 277a5ff..2ab97f9 100644
--- a/pkgs/test_core/lib/src/runner/runner_suite.dart
+++ b/pkgs/test_core/lib/src/runner/runner_suite.dart
@@ -69,7 +69,7 @@
   }
 
   /// Closes the suite and releases any resources associated with it.
-  Future close() => _controller._close();
+  Future<void> close() => _controller._close();
 
   /// Collects a hit-map containing merged coverage.
   ///
@@ -92,7 +92,7 @@
   final SuiteConfiguration _config;
 
   /// A channel that communicates with the remote suite.
-  final MultiChannel? _suiteChannel;
+  final MultiChannel<Object?>? _suiteChannel;
 
   /// The function to call when the suite is closed.
   final Function()? _onClose;
@@ -149,7 +149,7 @@
   /// This is exposed on the [RunnerSuiteController] so that runner plugins can
   /// communicate with the workers they spawn before the associated [suite] is
   /// fully loaded.
-  StreamChannel channel(String name) {
+  StreamChannel<Object?> channel(String name) {
     if (!_channelNames.add(name)) {
       throw StateError('Duplicate RunnerSuite.channel() connection "$name".');
     }
@@ -166,7 +166,7 @@
   }
 
   /// The backing function for [suite.close].
-  Future _close() => _closeMemo.runOnce(() async {
+  Future<void> _close() => _closeMemo.runOnce(() async {
         await _onDebuggingController.close();
         var onClose = _onClose;
         if (onClose != null) await onClose();
diff --git a/pkgs/test_core/lib/src/runner/runner_test.dart b/pkgs/test_core/lib/src/runner/runner_test.dart
index 10ef8bc..71e8844 100644
--- a/pkgs/test_core/lib/src/runner/runner_test.dart
+++ b/pkgs/test_core/lib/src/runner/runner_test.dart
@@ -29,14 +29,14 @@
   final Trace? trace;
 
   /// The channel used to communicate with the test's `RemoteListener`.
-  final MultiChannel _channel;
+  final MultiChannel<Object?> _channel;
 
   RunnerTest(this.name, this.metadata, this.trace, this._channel);
 
   @override
   LiveTest load(Suite suite, {Iterable<Group>? groups}) {
     late final LiveTestController controller;
-    late final VirtualChannel testChannel;
+    late final VirtualChannel<Object?> testChannel;
     controller = LiveTestController(suite, this, () {
       controller.setState(const State(Status.running, Result.success));
 
@@ -44,6 +44,7 @@
       _channel.sink.add({'command': 'run', 'channel': testChannel.id});
 
       testChannel.stream.listen((message) {
+        message as Map<String, Object?>;
         switch (message['type'] as String) {
           case 'error':
             var asyncError = RemoteException.deserialize(message['error']);
diff --git a/pkgs/test_core/lib/src/runner/spawn_hybrid.dart b/pkgs/test_core/lib/src/runner/spawn_hybrid.dart
index e21b8f8..685c693 100644
--- a/pkgs/test_core/lib/src/runner/spawn_hybrid.dart
+++ b/pkgs/test_core/lib/src/runner/spawn_hybrid.dart
@@ -31,7 +31,8 @@
 /// contains `pubspec.yaml`, *not* the `test/` directory). If it's a `package:`
 /// URL, it will be resolved using the current package's dependency
 /// constellation.
-StreamChannel spawnHybridUri(String url, Object? message, Suite suite) {
+StreamChannel<Object?> spawnHybridUri(
+    String url, Object? message, Suite suite) {
   return StreamChannelCompleter.fromFuture(() async {
     url = await _normalizeUrl(url, suite);
     var port = ReceivePort();
diff --git a/pkgs/test_core/lib/src/runner/vm/environment.dart b/pkgs/test_core/lib/src/runner/vm/environment.dart
index 057a03a..8f2c75c 100644
--- a/pkgs/test_core/lib/src/runner/vm/environment.dart
+++ b/pkgs/test_core/lib/src/runner/vm/environment.dart
@@ -25,10 +25,10 @@
   Uri? get remoteDebuggerUrl => null;
 
   @override
-  Stream get onRestart => StreamController.broadcast().stream;
+  Stream<void> get onRestart => StreamController.broadcast().stream;
 
   @override
-  CancelableOperation displayPause() {
+  CancelableOperation<void> displayPause() {
     var completer =
         CancelableCompleter(onCancel: () => _client.resume(_isolate.id!));
 
diff --git a/pkgs/test_core/lib/src/runner/vm/platform.dart b/pkgs/test_core/lib/src/runner/vm/platform.dart
index 2ea9483..5f82576 100644
--- a/pkgs/test_core/lib/src/runner/vm/platform.dart
+++ b/pkgs/test_core/lib/src/runner/vm/platform.dart
@@ -125,7 +125,7 @@
   }
 
   @override
-  Future close() => _closeMemo.runOnce(_compiler.dispose);
+  Future<void> close() => _closeMemo.runOnce(_compiler.dispose);
 
   Uri _absolute(String path) {
     final uri = p.toUri(path);
diff --git a/pkgs/test_core/lib/src/runner/wasm_compiler_pool.dart b/pkgs/test_core/lib/src/runner/wasm_compiler_pool.dart
index 50efeba..174dc02 100644
--- a/pkgs/test_core/lib/src/runner/wasm_compiler_pool.dart
+++ b/pkgs/test_core/lib/src/runner/wasm_compiler_pool.dart
@@ -26,7 +26,7 @@
   /// The returned [Future] will complete once the `dart2wasm` process completes
   /// *and* all its output has been printed to the command line.
   @override
-  Future compileInternal(
+  Future<void> compileInternal(
       String code, String path, SuiteConfiguration suiteConfig) {
     return withTempDir((dir) async {
       var wrapperPath = p.join(dir, 'main.dart');
diff --git a/pkgs/test_core/lib/src/util/io.dart b/pkgs/test_core/lib/src/util/io.dart
index da9f072..59b6ed0 100644
--- a/pkgs/test_core/lib/src/util/io.dart
+++ b/pkgs/test_core/lib/src/util/io.dart
@@ -105,7 +105,7 @@
 ///
 /// Returns a future that completes to the value that the future returned from
 /// [fn] completes to.
-Future withTempDir(Future Function(String) fn) {
+Future<void> withTempDir(Future<void> Function(String) fn) {
   return Future.sync(() {
     var tempDir = createTempDir();
     return Future.sync(() => fn(tempDir))
diff --git a/pkgs/test_core/lib/src/util/print_sink.dart b/pkgs/test_core/lib/src/util/print_sink.dart
index ebb2fe8..5ff561d 100644
--- a/pkgs/test_core/lib/src/util/print_sink.dart
+++ b/pkgs/test_core/lib/src/util/print_sink.dart
@@ -12,7 +12,7 @@
   }
 
   @override
-  void writeAll(Iterable objects, [String separator = '']) {
+  void writeAll(Iterable<Object?> objects, [String separator = '']) {
     _buffer.writeAll(objects, separator);
     _flush();
   }
diff --git a/pkgs/test_core/lib/src/util/stack_trace_mapper.dart b/pkgs/test_core/lib/src/util/stack_trace_mapper.dart
index 9e8650a..c87d41c 100644
--- a/pkgs/test_core/lib/src/util/stack_trace_mapper.dart
+++ b/pkgs/test_core/lib/src/util/stack_trace_mapper.dart
@@ -53,7 +53,7 @@
 
   /// Returns a [StackTraceMapper] contained in the provided serialized
   /// representation.
-  static StackTraceMapper? deserialize(Map? serialized) {
+  static StackTraceMapper? deserialize(Map<Object?, Object?>? serialized) {
     if (serialized == null) return null;
     var deserialized = _deserializePackageConfigMap(
         (serialized['packageConfigMap'] as Map).cast<String, String>());