Is it possible to assert that a variable is an array of strings?
papb opened this issue ยท 21 comments
Is it possible to assert that a variable is an array of strings, ideally with automatic typescript type assertion to string[]
? I could not find information about this in the readme. It does not seem possible.
If yes, this issue is a readme clarification request. If not, this issue is a feature request ๐ฌ
After a more cautious read, I found that assert.array<string>(foo)
will:
- Ensure
foo
is an array (of anything) - Upon success in the bullet above, make TS think the type is
string[]
So I guess I could use for now:
assert.array<string>(foo);
for (const element of foo) {
assert.string(element);
}
But it would be much better to have a simpler way to do it... ๐
Would you be willing to accept a PR for this feature?
Note: I did not explicitly specify how exactly the feature will look like in purpose; I will think more carefully if you say yes. Ideally something very powerful that can take other assertions as an extra argument and auto-apply it on each element and at the same time be smart on the asserted types...
I played a bit in the typescript playground, and what we can do is overload (or change) assert.array
to receive an assertion.
Code snippet:
declare function assertString(value: unknown): asserts value is string;
declare function assertArray<T>(
arr: unknown[],
assertion: (value: unknown) => asserts value is T,
): asserts arr is T[];
// asd is unknown[]
const asd: unknown[] = [];
assertArray(asd, assertString);
// asd is string[]
console.log(asd);
So you're more than welcome to open a PR ๐๐ป
Like always, make sure to test your code, and include any relevant changes in the documentation.
Discuss with us if you have any questions about implementation or api.
@sindresorhus any thoughts on what's the best choice for the api? add overload? new method entirely?
Hi @gioragutt, thanks for the fast response!! Very nice your work in the playground. I didn't know asserts
could work that nicely.
Honestly neither did I, I just tried to see if the syntax is valid and it is ๐คท๐ปโโ๏ธ
Hurray for good syntax design ๐
Sounds like a good feature. It will need a more specific proposal on how the final API should look like though (whether it should be a separate method or overload, and why / why not).
@sindresorhus Can I take this PR? what do you think the API should look like?
It will need a more specific proposal on how the final API should look like though (whether it should be a separate method or overload, and why / why not).
assert.arrayOfType(arr, is.string)
with the same signature as what @gioragutt suggested.
Is this ok?
I chose the name since typedArray naming is already taken and references something else.
Another option is assert.arrayItems(arr, is.string)
which doesn't imply that it's only meant for checking types.
Hi @Arnovsky, thanks for being willing to fix this! I will use this extensively.
Among arrayOfType
and arrayItems
, I prefer arrayItems
since as you said we can check things like evenInteger
which is not a type per se.
But what do you think of just overloading the already existing array
method? Since currently is.array
and assert.array
only expect one parameter, if you were to add a second parameter it wouldn't be an issue:
is.array(arr, is.string)
assert.array(arr, assert.string)
// or maybe
assert.array(arr, is.string)
@papb I like your idea, overloading seems like a better API than I suggest , the only question is if it's explicit enough.
Hmm, I see... Of course I am probably biased since I suggested the API, but I do not see any other possible interpretation for is.array(arr, is.string)
other than "array whose elements satisfy is.string
". Is it possible that someone would interpret it in another way?
If I had to suggest a name instead of an overload, I would say .arrayWhoseElementsSatisfy
, but that is too long...
@papb is.array(arr, is.string)
Sounds good to me. Is this fine with you? @sindresorhus
@Arnovsky and how do you feel about assert? assert.array(arr, is.string)
or assert.array(arr, assert.string)
? I would be fine with both I guess, whichever is easier to implement is ok to me.
Depends on the use case, but IMO both are needed.
I think assert.array(arr, assert.string)
is more consistent since we are asserting and not returning anything. To me it makes more sense API wise.
Looks good to me, is.array(arr, is.string)
and assert.array(arr, assert.string)
then ๐
Let's see what @sindresorhus says
๐
@papb @gioragutt After starting to implement I have realized that is.array is a property (same applies for assert.array) and it cannot be overloaded.
So I'll implement the arrayItems
method instead.
@Arnovsky I don't understand, I don't see why it wouldn't be overloadable, can you please clarify? Maybe I can help you figure out how to overload them
For example:
- is.array = Array.isArray;
+ is.array = (value: unknown, elementChecker?: Checker) => {
+ if (!elementChecker) {
+ return Array.isArray(value);
+ }
+ // ... your implementation
+ }