core-api: update ApiFactory type to correctly infer API type and disallow mismatched implementations
This commit is contained in:
@@ -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: () => ({}),
|
||||
})
|
||||
```
|
||||
@@ -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);
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user