I am trying to code a user defined type guard that check the types of the element of an array.
This is what I have:
const isArray = <T>(o: any | any[]): o is T[] => {
return Array.isArray(o)
}
So I can call it this way
const func = function(arg: string | string[] | number | number[]) {
if (isArray<string>(arg)) {
console.log(arg)
} else {
console.log(arg)
}
}
This check that arg is an array of string.
I can also use it this way:
const func = function(arg: string | string[] | number | number[]) {
if (isArray(arg)) {
console.log(arg)
} else {
console.log(arg)
}
}
And now, it's checking is an array (string[]
or number[]
here). But the problem it's that the type guard can be called with random generic type, for example Boolean
resulting in a weird behavior.
So I went for this function:
const isArray = <T extends U, U>(o: U | U[]): o is T[] => {
return typeof o === 'object' && typeof o['length'] === 'number'
}
So now, there is a constraint on the type guard. If I call it this way:
isArray<string, typeof arg>(arg)
I will have an error if the first generic type is not one of the arg types. This leads to a new issue.
const func = function(arg: string[] | number[]) {
if (isArray<string, typeof arg>(arg)) {
console.log(arg)
} else {
console.log(arg)
}
}
In this case, I will have an error because string
is not part of arg types.
So I have to issues:
typeof arg
) ? It would be great that it is hardcoded and that user doesn't have to provide it every time he uses the type guardstring | number
if isArray
is called with a parameter of type number[] | string[]
What you are trying to do makes little sense to me. Seems like your type guards are lying - they assert blindly that if something is an Array it's an Array of a given type.
Your type guard should check if the array members are indeed of the given type, and since you can't know the given type at runtime (which is when the type guard is running), you can't do it in a generic way.
For example, isStringArray
can be written like this:
const isStringArray = (arg: any): arg is string[] => {
return Array.isArray(arg) && arg.every(item => typeof item === 'string')
}
The check must be specific to the type you want to assert.