There is no possibility to do this right now.
If You trace the router code yourself begining from router#navigate(), You can see that create_url_tree#tree() function builds a tree with stringified queryParams:
function tree(oldSegmentGroup, newSegmentGroup, urlTree, queryParams, fragment) {
if (urlTree.root === oldSegmentGroup) {
return new UrlTree(newSegmentGroup, stringify(queryParams), fragment);
}
return new UrlTree(replaceSegment(urlTree.root, oldSegmentGroup, newSegmentGroup), stringify(queryParams), fragment);
}
And stringify() does all the dirty work:
function stringify(params) {
var /** @type {?} */ res = {};
forEach(params, function (v, k) { return res[k] = "" + v; });
return res;
}
Result of concatenation of string and array is a comma-delimited string.
There are a couple of issues about multiple query parameters with the same name, that was fixed in https://github.com/angular/angular/pull/11373.
As You can see, modified url_tree#serializeQueryParams() does exactly what You need. But the problem is that serialization takes place a lot later as the tree will be built.
It seems that this is a bug in create_url_tree#tree() - it shouldn't stringify query params, because it is the area of responsibility of url_tree#serializeQueryParams(). I've removed call to stringify() locally and everything started working as it should.
As workaround You can stringify each query parameter manually with JSON.stringify(queryParamArray) before sending it to router#naviagate() and parse it with JSON.parse(param) in route.queryParams.subscribe(). URL looks horrible, but now it's the only way to pass arrays.
UPDATE: I created the issue in the angular repository: https://github.com/angular/angular/issues/14796