Skip to content

Commit

Permalink
core-api: update ApiFactory type to correctly infer API type and disa…
Browse files Browse the repository at this point in the history
…llow mismatched implementations
  • Loading branch information
Rugvip committed Dec 4, 2020
1 parent 4266967 commit b6557c0
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 18 deletions.
23 changes: 23 additions & 0 deletions .changeset/calm-icons-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
'@backstage/core-api': patch
'@backstage/dev-utils': patch
---

Update ApiFactory type to correctly infer API type and disallow mismatched implementations.

This fixes for example the following code:

```ts
interface MyApi {
myMethod(): void
}

const myApiRef = createApiRef<MyApi>({...});

createApiFactory({
api: myApiRef,
deps: {},
// This should've caused an error, since the empty object does not fully implement MyApi
factory: () => ({}),
})
```
10 changes: 6 additions & 4 deletions packages/core-api/src/apis/system/ApiFactoryRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export class ApiFactoryRegistry implements ApiFactoryHolder {
* A factory will not be added to the registry if there is already
* an existing factory with the same or higher priority.
*/
register<Api, Deps extends { [name in string]: unknown }>(
register<Api, Impl extends Api, Deps extends { [name in string]: unknown }>(
scope: ApiFactoryScope,
factory: ApiFactory<Api, Deps>,
factory: ApiFactory<Api, Impl, Deps>,
) {
const priority = ScopePriority[scope];
const existing = this.factories.get(factory.api);
Expand All @@ -69,12 +69,14 @@ export class ApiFactoryRegistry implements ApiFactoryHolder {
return true;
}

get<T>(api: ApiRef<T>): ApiFactory<T, { [x: string]: unknown }> | undefined {
get<T>(
api: ApiRef<T>,
): ApiFactory<T, T, { [x: string]: unknown }> | undefined {
const tuple = this.factories.get(api);
if (!tuple) {
return undefined;
}
return tuple.factory as ApiFactory<T, { [x: string]: unknown }>;
return tuple.factory as ApiFactory<T, T, { [x: string]: unknown }>;
}

getAllApis(): Set<AnyApiRef> {
Expand Down
15 changes: 8 additions & 7 deletions packages/core-api/src/apis/system/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@ export function createApiFactory<
Api,
Impl extends Api,
Deps extends { [name in string]: unknown }
>(factory: ApiFactory<Api, Deps>): ApiFactory<Api, Deps>;
export function createApiFactory<Api>(
>(factory: ApiFactory<Api, Impl, Deps>): ApiFactory<Api, Impl, Deps>;
export function createApiFactory<Api, Impl extends Api>(
api: ApiRef<Api>,
instance: Api,
): ApiFactory<Api, {}>;
instance: Impl,
): ApiFactory<Api, Impl, {}>;
export function createApiFactory<
Api,
Impl extends Api,
Deps extends { [name in string]: unknown }
>(
factory: ApiFactory<Api, Deps> | ApiRef<Api>,
instance?: Api,
): ApiFactory<Api, Deps> {
factory: ApiFactory<Api, Impl, Deps> | ApiRef<Api>,
instance?: Impl,
): ApiFactory<Api, Impl, Deps> {
if ('id' in factory) {
return {
api: factory,
Expand Down
16 changes: 12 additions & 4 deletions packages/core-api/src/apis/system/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,24 @@ export type ApiHolder = {
get<T>(api: ApiRef<T>): T | undefined;
};

export type ApiFactory<Api, Deps extends { [name in string]: unknown }> = {
export type ApiFactory<
Api,
Impl extends Api,
Deps extends { [name in string]: unknown }
> = {
api: ApiRef<Api>;
deps: TypesToApiRefs<Deps>;
factory(deps: Deps): Api;
factory(deps: Deps): Impl;
};

export type AnyApiFactory = ApiFactory<unknown, { [key in string]: unknown }>;
export type AnyApiFactory = ApiFactory<
unknown,
unknown,
{ [key in string]: unknown }
>;

export type ApiFactoryHolder = {
get<T>(
api: ApiRef<T>,
): ApiFactory<T, { [key in string]: unknown }> | undefined;
): ApiFactory<T, T, { [key in string]: unknown }> | undefined;
};
8 changes: 5 additions & 3 deletions packages/dev-utils/src/devApp/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ class DevAppBuilder {
/**
* Register an API factory to add to the app
*/
registerApi<Api, Deps extends { [name in string]: unknown }>(
factory: ApiFactory<Api, Deps>,
): DevAppBuilder {
registerApi<
Api,
Impl extends Api,
Deps extends { [name in string]: unknown }
>(factory: ApiFactory<Api, Impl, Deps>): DevAppBuilder {
this.apis.push(factory);
return this;
}
Expand Down

0 comments on commit b6557c0

Please sign in to comment.