I would like to have a generic type predicate that allows me to check the type of an object's property.
To illustrate this, I want to achieve the following:
const a: { [key in string]: string | string[]} = {
arrayProp: ["this", "is", "an", "array"],
otherProp: "this is a string"
};
Object.keys(a).forEach((prop: keyof typeof a) => {
if (isArrayProperty(a, prop)) {
// `a[prop]` is correctly detected as array
a[prop].push("another value")
}
})
I was expecting something like this to work
function isArrayProperty<T, P extends keyof T>(
obj: T,
prop: P,
): T[P] is Array<any> {
return Array.isArray(obj[prop]);
}
However TypeScript seems to have problems with the generics and is statement in the return type.
Additional notes
I know I could just pass the value to a function like Array.isArray(a["arrayProp"]) to get it work.
However, I want to go even one step further where I pass in a constructor and a property to see whether the property of an object is an array type:
type ClassConstr<T> = new (...props) => T
function isArrayProperty<T, P extends keyof T>(
obj: ClassConstr<T>,
prop: P,
): T[P] is Array<any> {
return // some magic happening here;
}
class A {
someProp: any;
}
const a = new A()
a = ["some", "array"];
if (isArrayProperty(A, "someProp") {
// `a.someProp` is correctly detected as array
a.someProp.push("new value");
}
The background is, that I have a separate schema definition for my classes that is only available at runtime. These schema definitions then decide, whether a property is an array, a string, a date, ... Therefore, I would like to have a function that allows me to still achieve type-safety in the components where I use these classes.