Closed AnsgarLichter closed 1 week ago
Hi Ansgar,
thanks for your report! This actually has to be viewed from another perspective: the .ts files are the ones that are wrong. What you see in the .js files is what you will actually get during runtime. There are no classes. We just present these classes on type level to give the user a better feel for their model at design time. But these classes do not actually exist at runtime and can not be shoehorned into cds. They are therefore meant purely as a design time artefact and are not supposed to be treated as actual classes to inherit from.
I guess there is an XY-problem at play here. Why do you need to extend the generated classes?
Best, Daniel
Hi Daniel,
thanks for your response - sorry for the wrong classification as bug
.
I'm extending the generated classes to add methods that are needed for our model because it makes sense from an OOP perspective to include these methods in the classes.
The advantage of using those generated classes is that you don't have to manually repeat the property names and adapt them when the corresponding cds
model is changed - this happens automatically via cds-typer
.
Of course, you have to create an instance after selecting the data manually to be able to use those methods:
const data = await SELECT
.from(Books);
const book = new Book(data);
book.greet();
Alternatively it is also possible to just design the class as follows without extending but the first approach looks better:
import { Book as bookModel } from "#cds-models/sap/capire/bookshop";
export default class Books {
#data: booksModel
constructor(data: BooksModel) {
this.#data = data;
}
public greet() {
console.log(`You are reading ${this.#data.title}`);
}
}
As the first approach works during runtime, locally and also in a deployed state with the workaround, I thought that this is not a problem of what you get during runtime, rather of the transpilations results. Maybe JavaScript's nature helps to cover some things happening in the background.
Best, Ansgar
Hi Ansgar,
I'm not an avid cds user myself, so more proficient users can probably provide more insight on this. But this doesn't look like an idiomatic service implementation. I have not seen model-data handling outside of a service's handler function so far, where all of the instances are passed in as parameter of that handler:
class MyService extends cds.ApplicationService { init () {
this.on('READ', Book, data => { /* use the "instances" in here */ }
}}
The functionality you showed is achievable by passing your entities to an arbitrary function:
function greet (b: Book) {
console.log(`You are reading ${b.title}`);
}
const book = await SELECT.one.from(Books)
greet(book)
This should always be possible, as all properties in the generated classes are public, so no strict data encapsulation is taking place.
I wouldn't say the cds runtime strictly embraces the OOP paradigm, to be honest. The fact that we chose classes to represent cds artefacts during runtime is purely for convenience[^1]. Entity instances are POJOs, rather than class instances.
Does this help?
Best, Daniel
[^1]: we could have used type
s instead of class
es, but we were facing incompatibility issues with pure JS projects at the time.
Hi Daniel,
please excuse the late response. In larger projects it makes sense to distribute your code to keep the code readable and understandable. As there are many ways to organise it, we tried to organise our code like it is done in OOP and then stumbled on this behaviour. Obviously, there are also many other very valid ways to achieve this.
In my opinion, the JS used in the deployed state should correspond to the TypeScript used for development because otherwise you loose some of TypeScript's great benefits. Nevertheless, I understand why the build is developed at it is, thanks to your explanation.
I think we can close this issue. There is a workaround and I don't think we will fully agree on each other's POVs.
Best, Ansgar
Hi Ansgar,
glad you found the workaround to be at least somewhat feasible for your case!
Just to clarify: cds-typer is purely a design time library layered on top of the cds runtime. As such, there is no way for me to control how cds behaves during runtime (and is therefore not subject to my personal opinion). If you believe there should be a change in how cds behaves, then you need to approach the cds runtime team. cds-typer just follows suit.
Best, Daniel
Is there an existing issue for this?
Nature of Your Project
TypeScript
Current Behavior
The TypeScript build plugin copies the untranspiled model files from
@cds-models
to the output directory, without any option to customize this behaviour.For example, the generated TypeScript file looks like this:
cds-typer
does generate the following js file (for JavaScript projects):This does not include the typings when I want to depend on the types, e. g. in the following sample:
tsc
does not report an error during development. As soon ascds-ts watch
is used to execute the project, this leads to the following error:This is due to the fact that the generated js file doesn't correspond to the transpilation result, when
tsc
is executed for this file. Therefore, I changed mytsconfig.json
thatts-node
transpiles@cds-models
' TypeScript files:For this to work, install
npm i -D tsconfig-paths
. Now this code works, whencds-ts watch
is used. If I know usecds build
to build my project for deployment,tsc
is also used and copies the transpiled models to the output directory with the tsconfig above. Nevertheless, after tsc is finished the plugin copies the original@cds-model
files to thegen
folder which then leads to the error shown above when executed:The workaround is to modify the executed commands during build as following:
Now this works as expected and the transpiled js files from
@cds-model
are used instead of the untranspiled files. In my example the filegen/@cds-models/sap/capire/bookshop/index.js
looks like the following:Expected Behavior
Expected behaviour is that
tsc
is used to transpile the TypeScript files and instead of copying the untranspiled JavaScript files when executed in a TypeScript cds project or at least an option incds.env.build
is given so that this behaviour can be customized. So in my use case the linethis.#copyCleanModel(buildDirCdsModels)
could be removed in the build plugin and everything would work fine. As I think there is a reason for this line to exist, I file this issue to gather more insights how to mitigate this issue.Steps To Reproduce
cds build --production
and vaildate the result ingen/@cds-models/sap/capire/bookshop/index.js
Environment
Repository Containing a Minimal Reproducible Example
No response
Anything else?
No response