TL;DR - The super keyword in my class declaration is sometimes (but not always) undefined. How/why?
I have the following class declaration extending Array
used for search result data from an API:
class SearchResult extends Array {
constructor(resultArray) {
super(...resultArray);
}
static createFromApiData(apiData) {
const resultArray = apiData.filter(...).map(...);
return new SearchResult(resultArray);
}
...otherMethods
}
In my app this is initially called immediately on receiving the response from the API, like below:
apiCall().then((res) => {
const searchResults = SearchResult.createFromApiData(res.data);
});
Creating a new SearchResult
instance using this static method works correctly, returning what I want.
However, later in my app I need to create a SearchResult
instance again, this time there is no need for an API call as I already have the resultArray
data stored in a database. Therefore the second time around I try to create the instance of SearchResult
using the new
keyword like below:
const searchResults = new SearchResult(resultArray);
This time, using the new
keyword the instance creation fails throwing the following error:
TypeError: undefined is not a function at new SearchResult
I know undefined
in this context is referring to the super
keyword but I can not understand why this is the case? How can the instance creation behaviour vary in these two cases? How can I fix it?
UPDATE
I've investigated further and found the following. My initial issue was caused because I was saving my custom SearchResult
class instance to the database (dynamodb). Although SearchResult
extended array, ddb saved it as a basic object, so on retrieval of the data from the database it came in basic object, not array form. This then threw an error when I attempted to spread the object inside inside the super
function.
I resolved this by spreading my class instance in a new array when saving it to ddb:
db.save([...searchResults]);
This returned an array after database recall and could be successfully spread inside the super
call.
However, this has lead to a new issue. Immediately after creating the new SearchResult
instance I call a the following custom method on it:
nextItem() {
this.splice(0, 1);
return this[0];
}
This method then again calls super
to access the splice
method on Array
. However, once again this fails throwing the same error as before:
{
"errorMessage": "undefined is not a function",
"errorType": "TypeError",
"stackTrace": [
"new SearchResult",
"SearchResult.splice",
"SearchResult.nextRecipe"
}
Once again this is referring to the spread operation inside super
. This time super
is being passed the number 1
as its sole argument. Where is is this 1
coming from and how can I fix it?
UPDATE 2
After updating the class method to us shift
instead of splice
the method now works correctly without errors. My problem is now effectively solved, however I would still appreciate insight on what the issue of super
being called with 1
was about with splice
. Why would these two array methods behave differently in this context.
nextItem() {
return this.shift();
}
I know undefined in this context is referring to the super keyword but I can not understand why this is the case?
Probably not. super
refers to the internal unchangeable [[HomeObject]] property of the class, that gets set when the class gets declared, you can't modify that, you can only rewrite the class itself, e.g.:
SearchResult = function() { undefined(); }
But I guess you don't do that.
But where does the error come from?
Well there are two things happening in that line:
super(...resultArray);
One is the super()
call and the other one is the spreading of the resultArray
, which will create and consume an iterator of the resultArray
which is barely equal to:
resultArray[Symbol.iterator]()
and if the iterator does not exist (because you accidently passed a non array to the constructor) it fails, saying that the iterator can't be called:
var resultArray = {};
resultArray[Symbol.iterator]()
// equals:
undefined()