Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use interface for cds_services to allow module augmentation #185

Open
aschmidt93 opened this issue Aug 5, 2024 · 2 comments · May be fixed by #186
Open

Use interface for cds_services to allow module augmentation #185

aschmidt93 opened this issue Aug 5, 2024 · 2 comments · May be fixed by #186

Comments

@aschmidt93
Copy link

cds.services is currently typed as type cds_services: export type cds_services = { [name: string]: Service }. Types can not be extended via module augmentation, thus this does not work:

// index.d.ts
import "@sap/cds";

declare module "@sap/cds" {
  export declare type cds_services = { foo: "bar" };
}

cds.services would still be typed as { [name: string]: Service }.

In order for module augmentation to work, cds_services has to be an interface.

Idea

declare module "@sap/cds" {
  type service_dict = { [name: string]: Service }
  export interface cds_services extends service_dict {}
}

Now module augmentation can be used to extend cds_services:

// index.d.ts
import "@sap/cds";

declare module "@sap/cds" {
  export interface cds_services {
    foo: "bar";
  }
}

Now, cds.services.foo is properly typed as "bar".

Reason

This can be used to narrow down the type of cds_services to only those services that actually exist in the project. Additionally, cds-typer can be used to type those services.

@daogrady
Copy link
Contributor

Hi Andreas,

thank you for your suggestion. I am actually not sure if module augmentation is the right approach here, as that is kind of a "last resort" technique and here it is used for added convenience. Wouldn't a helper type appropriately address the underlying issue?

Best,
Daniel

@aschmidt93
Copy link
Author

Hi Daniel,

sorry for the late response. I do think module augmentation is the right approach here unless there's a way for cds-types to automatically pick up the services generated by cds-typer and type cds_services accordingly.

The alternative would be to manually type each time a service is accessed:

// `OrderServiceTypes` is the file generated by cds-typer (import type * as OrderServiceTypes from "...")
// TypedService is a custom type that uses the output of cds-typer to type the generic methods of a service, e.g. `send` or `emit`
const orderService : TypedService<typeof OrderServiceTypes> = cds.services["OrderService"];

// compile time checked with `TypedService`
await orderService.emit("foo", {bar : 0});

I think this is cumbersome and prone to error, especially in a larger code base with many service-to-service interactions. Therefore, module augmentation fits perfectly. It allows users to define once in a central place which services are available and additionally users can type those services, if they choose to. Additionally, it requires no runtime code. And the change required in cds-types is miniscule, isn't it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants