You could pass an array to the function instead of using rest parameters:
public static all(values: unknown[]): values is object[] {
return values.every(value => typeof(value) !== 'undefined');
}
However the code is technically incorrect since you are asserting that the values are objects when you don't know anything beyond the fact that they are not undefined. You should either check if typeof is equal to 'object' instead or correct the return type:
public static all(values: unknown[]): values is (string|number|object|symbol|boolean)[] {
return values.every(value => value !== null && typeof value !== 'undefined');
}
}
Note that typeof null === "object"
unfortunately so neither comparisson will weed out null values. I've taken the liberty of adding a null check above.
Now if you use the code as such:
if (!ParamHelper.all([id, ...dateParts])) { return []}
const date = new Date(dateParts.join('-'));
And you go and hover over dateParts in the second line, you will see that the typeguard did not work. Frustratingly dateParts
is still undefined. But it's easy to understand why and it might help you understand why it doesn't work with the rest param:
const checked = [id, ...dateParts];
if (!ParamHelper.all(checked)) return [];
const checkedDateParts = checked.slice(1);
const date = new Date(checkedDateParts.join('-'));
Here if you hover over checked or checkedDateParts you will notice that they are of the expected type. In the case above as well as with the rest parameter the array whose type is being determined is thrown away at the end of the function call.
Typescript is not yet smart enough to retroactively pass down typing asserted on an array into the variables that were used to construct it. Given that the array might be constructed via conditional or iterative logic its reasonable to expect it will never be able to do so.
The same is true for the rest parameter, the values array is created implicitly when the function is called and destroyed as the function returns.
In the example above we keep the reference to the array and therefore also the type, we then extract a typed slice from the typed array. We are not going backwards in time to change the type of dateParts. It is still typed as unknown[].
Personally I think using a type-guard here is overengineering. Given that the context is a date, the code you provided in your comment:
if (!id || !day || !month || !year) { return; }
Is much clearer, shorter and easier to maintain.
If you must use the type guard I would go with something like:
if (!id || !ParamHelper.all(dateParts)) { return }
const date = new Date(dateParts.join('-'));
playground link
const a = [id, ...dateParts]; if (!all(a){}{})
whereall
is of type(x: unknown[])=>x is object[]
. (note thatnull
is not considered assignable toobject
in TypeScript; might be a wrinkle in your implementation.) β Coverallif (!id || !day || !month || !year) { return; }
. β Gigigigli