I am trying to call the function of the parent class from the child but the keyword super
is throwing and error. I am using typescript, here is a snippet of the package.json from the project.
{
"scripts": {
"build": "tsc",
"start": "nodemon",
"prod": "npm run build && npm run start"
},
"dependencies": {
"body-parser": "^1.18.3",
"dotenv": "^6.1.0",
"express": "^4.16.4"
},
"devDependencies": {
"@types/body-parser": "^1.17.0",
"@types/dotenv": "^4.0.3",
"@types/express": "^4.16.0",
"@types/node": "^10.12.2",
"nodemon": "^1.18.5",
"ts-node": "^7.0.1",
"tslint": "^5.11.0",
"typescript": "^3.1.6"
}
}
The parent class
export default class baseController {
public response = (message = "", status = 200) => (
req: Request,
res: Response
) => {
return res.status(status).send({
status: true, // true if success, false if faliure
message: message, // message to display incase of error
payload: []
});
};
}
The child class
import BaseController from "../baseController";
export default class UsersController extends BaseController {
constructor() {
super();
}
public fetchUsers = async () => {
return super.response("testing");
};
}
The code crashes on the line return super.response("testing");
with the error super keyword unexpected here
.
Here is my tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "./dist",
"pretty": true,
"baseUrl": "./src",
"alwaysStrict": true,
"paths": {
"*": ["node_modules/*", "src/types/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
This case is one of several reasons why prototype methods may be preferred over arrow class fields (instance methods), as explained in this answer.
There are several problems here.
One problem is that there's no super.response
. super
refers to parent class prototype, while response
is instance method.
Another problem is that the target is ES6. async
is transpiled to generators, and super
is not transpiled, this results in incorrect code:
fetchUsers.a = () => __awaiter(this, void 0, void 0, function* () { return super.response("testing") });
Only arrow functions can get super
from parent scope, super
is not allowed inside non-arrow functions. Since there are no arrow generators, super
use is invalid inside a generator. While TypeScript is able to handle super
properly in async
prototype methods:
fetchUsers() {
const _super = name => super[name];
return __awaiter(this, void 0, void 0, function* () { _super.response("testing").call(this); });
}
Yet another problem is that referring to super
in a class that doesn't override response
is semantic mistake. Child class already inherits response
. It can be used as this
method.
It should be:
export default class baseController {
public response(message = "", status = 200) (...) { ... }
}
export default class UsersController extends BaseController {
public async fetchUsers() {
return this.response("testing");
};
}
If fetchUsers
is expected to be used as a callback (this is the only good use for arrow methods), it should be bound to this
context in constructor:
public fetchUsers = this.fetchUsers.bind(this);
public async fetchUsers() {
return this.response("testing");
};
Where fetchUsers
class field is sugar syntax for constructor body.