Skip to content

Commit

Permalink
Forgot to actually use provide(); make it do the type cast!
Browse files Browse the repository at this point in the history
  • Loading branch information
chancancode committed Nov 5, 2023
1 parent 40897e6 commit fdabf27
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 13 deletions.
2 changes: 1 addition & 1 deletion ember-polaris-service/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { factory } from './factory.ts';
export { lookup, override } from './primitives.ts';
export { lookup, provide, override } from './primitives.ts';
export {
type ServiceFactory,
type ServiceManager,
Expand Down
3 changes: 3 additions & 0 deletions ember-polaris-service/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export interface ServiceFactory<T> {
[INSTANTIATE]: (scope: Scope) => T;
}

export type ServiceInstanceType<T extends ServiceFactory<unknown>> =
T extends ServiceFactory<infer Instance> ? Instance : unknown;

export interface ServiceManager<D extends object, T> {
createService(definition: D): T;
}
Expand Down
10 changes: 6 additions & 4 deletions ember-polaris-service/src/primitives.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { assert, runInDebug } from '@ember/debug';
import {
type ServiceFactory,
type ServiceInstanceType,
instantiate,
isServiceFactory,
} from './manager.ts';
Expand All @@ -24,11 +25,12 @@ export function lookup<T>(scope: Scope, factory: ServiceFactory<T>): T {
return service;
}

export function provide<T, U>(
factory: ServiceFactory<T>,
provider: ServiceFactory<T & U>,
): void {
export function provide<T1 extends ServiceFactory<unknown>, T2 extends T1>(
factory: T1,
provider: T2,
): ServiceFactory<ServiceInstanceType<T2>> {
Providers.set(factory, provider);
return factory as ServiceFactory<ServiceInstanceType<T2>>;
}

export function override<T>(
Expand Down
25 changes: 21 additions & 4 deletions test-app/app/services/json-storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ServiceFactory, service } from 'ember-polaris-service';
import { type ServiceFactory, provide, service } from 'ember-polaris-service';
import JSONStorage, { type Deserializer } from 'ember-storage-json';
import Storage from './storage';

Expand All @@ -25,7 +25,24 @@ export class AppJSONStorage extends JSONStorage {
}
}

// We must export the original well-known token!
// We must export the original well-known token! `provide` returns that.
//
// The `as ServiceFactory<AppJSONStorage>` here makes other parts of the app
// automatically
export default JSONStorage as ServiceFactory<unknown> as ServiceFactory<AppJSONStorage>;
// automatically.
//
// The type signature in `provide` is intended to make this just work without
// the type cast, and it does work for factory functions and stuff. So e.g.
// `provide(singleton(localStorage), factory(() => extendedStorage)) actually
// does work the way you expect.
//
// However, because of how classes work in TypesScript, all subclasses of
// Service are just considered `ServiceFactory<Service>`. The overload in
// `service` (the function in `service.ts`) is what actually make things
// work.
//
// We can add a similar overload for the primitives too, but I can't do that
// easily right now for circular dependencies reason.
export default provide(
JSONStorage,
AppJSONStorage,
) as ServiceFactory<AppJSONStorage>;
27 changes: 23 additions & 4 deletions test-app/app/services/storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type ServiceFactory, service, singleton } from 'ember-polaris-service';
import {
type ServiceFactory,
provide,
service,
singleton,
} from 'ember-polaris-service';
import Storage from 'ember-storage';
import { enable } from 'ember-storage/extensions';
import { type StorageWithBatching } from 'ember-storage-batch';
Expand Down Expand Up @@ -81,7 +86,21 @@ export class AppStorage extends Storage implements StorageWithBatching {
// you will still get an error here.
enable(AppStorage, 'batch');

// We must export the original well-known token!
// We must export the original well-known token! `provide` returns that.
//
// The `as ServiceFactory<AppStorage>` here makes other parts of the app
// automatically
export default Storage as ServiceFactory<unknown> as ServiceFactory<AppStorage>;
// automatically.
//
// The type signature in `provide` is intended to make this just work without
// the type cast, and it does work for factory functions and stuff. So e.g.
// `provide(singleton(localStorage), factory(() => extendedStorage)) actually
// does work the way you expect.
//
// However, because of how classes work in TypesScript, all subclasses of
// Service are just considered `ServiceFactory<Service>`. The overload in
// `service` (the function in `service.ts`) is what actually make things
// work.
//
// We can add a similar overload for the primitives too, but I can't do that
// easily right now for circular dependencies reason.
export default provide(Storage, AppStorage) as ServiceFactory<AppStorage>;

0 comments on commit fdabf27

Please sign in to comment.