Merge pull request #3950 from NoelDeMartin/MOBILE-4272
MOBILE-4272 workshop: Decouple from initial bundlemain
commit
cca9a3b784
|
@ -15,9 +15,7 @@
|
|||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate';
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import {
|
||||
AddonModWorkshopAssessmentStrategyAccumulativeHandler,
|
||||
} from '@addons/mod/workshop/assessment/accumulative/services/handler-lazy';
|
||||
import { getAssessmentStrategyHandlerInstance } from './services/handler';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -28,12 +26,7 @@ import {
|
|||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useValue: () => {
|
||||
// TODO use async instances
|
||||
// AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(
|
||||
AddonModWorkshopAssessmentStrategyAccumulativeHandler.instance,
|
||||
);
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_NAME,
|
||||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_STRATEGY_NAME,
|
||||
} from '@addons/mod/workshop/assessment/constants';
|
||||
import type { AddonModWorkshopAssessmentStrategyAccumulativeHandlerLazyService } from './handler-lazy';
|
||||
|
||||
export class AddonModWorkshopAssessmentStrategyAccumulativeHandlerService {
|
||||
|
||||
|
@ -32,13 +33,23 @@ export class AddonModWorkshopAssessmentStrategyAccumulativeHandlerService {
|
|||
* @returns Assessment strategy handler.
|
||||
*/
|
||||
export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler {
|
||||
const lazyHandler = asyncInstance(async () => {
|
||||
const lazyHandler = asyncInstance<
|
||||
AddonModWorkshopAssessmentStrategyAccumulativeHandlerLazyService,
|
||||
AddonModWorkshopAssessmentStrategyAccumulativeHandlerService
|
||||
>(async () => {
|
||||
const { AddonModWorkshopAssessmentStrategyAccumulativeHandler } = await import('./handler-lazy');
|
||||
|
||||
return AddonModWorkshopAssessmentStrategyAccumulativeHandler.instance;
|
||||
});
|
||||
|
||||
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyAccumulativeHandlerService());
|
||||
lazyHandler.setLazyInstanceMethods([
|
||||
'isEnabled',
|
||||
'getComponent',
|
||||
'getOriginalValues',
|
||||
'hasDataChanged',
|
||||
'prepareAssessmentData',
|
||||
]);
|
||||
|
||||
return lazyHandler;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate';
|
||||
import { AddonModWorkshopAssessmentStrategyCommentsHandler } from '@addons/mod/workshop/assessment/comments/services/handler-lazy';
|
||||
import { getAssessmentStrategyHandlerInstance } from './services/handler';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -26,10 +26,7 @@ import { AddonModWorkshopAssessmentStrategyCommentsHandler } from '@addons/mod/w
|
|||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useValue: () => {
|
||||
// TODO use async instances
|
||||
// AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(AddonModWorkshopAssessmentStrategyCommentsHandler.instance);
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_NAME,
|
||||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_STRATEGY_NAME,
|
||||
} from '@addons/mod/workshop/assessment/constants';
|
||||
import type { AddonModWorkshopAssessmentStrategyCommentsHandlerLazyService } from './handler-lazy';
|
||||
|
||||
export class AddonModWorkshopAssessmentStrategyCommentsHandlerService {
|
||||
|
||||
|
@ -32,13 +33,23 @@ export class AddonModWorkshopAssessmentStrategyCommentsHandlerService {
|
|||
* @returns Assessment strategy handler.
|
||||
*/
|
||||
export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler {
|
||||
const lazyHandler = asyncInstance(async () => {
|
||||
const lazyHandler = asyncInstance<
|
||||
AddonModWorkshopAssessmentStrategyCommentsHandlerLazyService,
|
||||
AddonModWorkshopAssessmentStrategyCommentsHandlerService
|
||||
>(async () => {
|
||||
const { AddonModWorkshopAssessmentStrategyCommentsHandler } = await import('./handler-lazy');
|
||||
|
||||
return AddonModWorkshopAssessmentStrategyCommentsHandler.instance;
|
||||
});
|
||||
|
||||
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyCommentsHandlerService());
|
||||
lazyHandler.setLazyInstanceMethods([
|
||||
'isEnabled',
|
||||
'getComponent',
|
||||
'getOriginalValues',
|
||||
'hasDataChanged',
|
||||
'prepareAssessmentData',
|
||||
]);
|
||||
|
||||
return lazyHandler;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate';
|
||||
import {
|
||||
AddonModWorkshopAssessmentStrategyNumErrorsHandler,
|
||||
} from '@addons/mod/workshop/assessment/numerrors/services/handler-lazy';
|
||||
import { getAssessmentStrategyHandlerInstance } from './services/handler';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -28,12 +26,7 @@ import {
|
|||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useValue: () => {
|
||||
// TODO use async instances
|
||||
// AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(
|
||||
AddonModWorkshopAssessmentStrategyNumErrorsHandler.instance,
|
||||
);
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_NAME,
|
||||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_STRATEGY_NAME,
|
||||
} from '@addons/mod/workshop/assessment/constants';
|
||||
import type { AddonModWorkshopAssessmentStrategyNumErrorsHandlerLazyService } from './handler-lazy';
|
||||
|
||||
export class AddonModWorkshopAssessmentStrategyNumErrorsHandlerService {
|
||||
|
||||
|
@ -32,13 +33,23 @@ export class AddonModWorkshopAssessmentStrategyNumErrorsHandlerService {
|
|||
* @returns Assessment strategy handler.
|
||||
*/
|
||||
export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler {
|
||||
const lazyHandler = asyncInstance(async () => {
|
||||
const lazyHandler = asyncInstance<
|
||||
AddonModWorkshopAssessmentStrategyNumErrorsHandlerLazyService,
|
||||
AddonModWorkshopAssessmentStrategyNumErrorsHandlerService
|
||||
>(async () => {
|
||||
const { AddonModWorkshopAssessmentStrategyNumErrorsHandler } = await import('./handler-lazy');
|
||||
|
||||
return AddonModWorkshopAssessmentStrategyNumErrorsHandler.instance;
|
||||
});
|
||||
|
||||
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyNumErrorsHandlerService());
|
||||
lazyHandler.setLazyInstanceMethods([
|
||||
'isEnabled',
|
||||
'getComponent',
|
||||
'getOriginalValues',
|
||||
'hasDataChanged',
|
||||
'prepareAssessmentData',
|
||||
]);
|
||||
|
||||
return lazyHandler;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate';
|
||||
import { AddonModWorkshopAssessmentStrategyRubricHandler } from '@addons/mod/workshop/assessment/rubric/services/handler-lazy';
|
||||
import { getAssessmentStrategyHandlerInstance } from './services/handler';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -26,10 +26,7 @@ import { AddonModWorkshopAssessmentStrategyRubricHandler } from '@addons/mod/wor
|
|||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useValue: () => {
|
||||
// TODO use async instances
|
||||
// AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(AddonModWorkshopAssessmentStrategyRubricHandler.instance);
|
||||
AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance());
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_NAME,
|
||||
ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_STRATEGY_NAME,
|
||||
} from '@addons/mod/workshop/assessment/constants';
|
||||
import type { AddonModWorkshopAssessmentStrategyRubricHandlerLazyService } from './handler-lazy';
|
||||
|
||||
export class AddonModWorkshopAssessmentStrategyRubricHandlerService {
|
||||
|
||||
|
@ -32,13 +33,23 @@ export class AddonModWorkshopAssessmentStrategyRubricHandlerService {
|
|||
* @returns Assessment strategy handler.
|
||||
*/
|
||||
export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler {
|
||||
const lazyHandler = asyncInstance(async () => {
|
||||
const lazyHandler = asyncInstance<
|
||||
AddonModWorkshopAssessmentStrategyRubricHandlerLazyService,
|
||||
AddonModWorkshopAssessmentStrategyRubricHandlerService
|
||||
>(async () => {
|
||||
const { AddonModWorkshopAssessmentStrategyRubricHandler } = await import('./handler-lazy');
|
||||
|
||||
return AddonModWorkshopAssessmentStrategyRubricHandler.instance;
|
||||
});
|
||||
|
||||
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyRubricHandlerService());
|
||||
lazyHandler.setLazyInstanceMethods([
|
||||
'isEnabled',
|
||||
'getComponent',
|
||||
'getOriginalValues',
|
||||
'hasDataChanged',
|
||||
'prepareAssessmentData',
|
||||
]);
|
||||
|
||||
return lazyHandler;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
} from '@addons/mod/workshop/constants';
|
||||
import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
|
||||
import { CoreCourseModulePrefetchHandler } from '@features/course/services/module-prefetch-delegate';
|
||||
import type { AddonModWorkshopPrefetchHandlerLazyService } from './prefetch-lazy';
|
||||
|
||||
export class AddonModWorkshopPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
|
||||
|
||||
|
@ -37,13 +38,17 @@ export class AddonModWorkshopPrefetchHandlerService extends CoreCourseActivityPr
|
|||
* @returns Prefetch handler.
|
||||
*/
|
||||
export function getPrefetchHandlerInstance(): CoreCourseModulePrefetchHandler {
|
||||
const lazyHandler = asyncInstance(async () => {
|
||||
const lazyHandler = asyncInstance<
|
||||
AddonModWorkshopPrefetchHandlerLazyService,
|
||||
AddonModWorkshopPrefetchHandlerService
|
||||
>(async () => {
|
||||
const { AddonModWorkshopPrefetchHandler } = await import('./prefetch-lazy');
|
||||
|
||||
return AddonModWorkshopPrefetchHandler.instance;
|
||||
});
|
||||
|
||||
lazyHandler.setEagerInstance(new AddonModWorkshopPrefetchHandlerService());
|
||||
lazyHandler.setLazyInstanceMethods(['sync']);
|
||||
|
||||
return lazyHandler;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import { asyncInstance } from '@/core/utils/async-instance';
|
||||
import { ADDON_MOD_WORKSHOP_SYNC_CRON_NAME } from '@addons/mod/workshop/constants';
|
||||
import { CoreCronHandler } from '@services/cron';
|
||||
import type { AddonModWorkshopSyncCronHandlerLazyService } from './sync-cron-lazy';
|
||||
|
||||
export class AddonModWorkshopSyncCronHandlerService {
|
||||
|
||||
|
@ -28,13 +29,17 @@ export class AddonModWorkshopSyncCronHandlerService {
|
|||
* @returns Cron handler.
|
||||
*/
|
||||
export function getCronHandlerInstance(): CoreCronHandler {
|
||||
const lazyHandler = asyncInstance(async () => {
|
||||
const lazyHandler = asyncInstance<
|
||||
AddonModWorkshopSyncCronHandlerLazyService,
|
||||
AddonModWorkshopSyncCronHandlerService
|
||||
>(async () => {
|
||||
const { AddonModWorkshopSyncCronHandler } = await import('./sync-cron-lazy');
|
||||
|
||||
return AddonModWorkshopSyncCronHandler.instance;
|
||||
});
|
||||
|
||||
lazyHandler.setEagerInstance(new AddonModWorkshopSyncCronHandlerService());
|
||||
lazyHandler.setLazyInstanceMethods(['execute', 'getInterval']);
|
||||
|
||||
return lazyHandler;
|
||||
}
|
||||
|
|
|
@ -114,3 +114,11 @@ Feature: Test basic usage of workshop activity in app
|
|||
And I pull to refresh in the app
|
||||
Then I should find "Closed" in the app
|
||||
And I should find "Conclusion 1" in the app
|
||||
|
||||
Scenario: Prefetch a workshop
|
||||
Given I entered the workshop activity "workshop" on course "Course 1" as "teacher1" in the app
|
||||
When I press "Information" in the app
|
||||
And I press "Download" in the app
|
||||
And I press "Close" in the app
|
||||
And I press the back button in the app
|
||||
Then I should find "Downloaded" in the app
|
||||
|
|
|
@ -27,8 +27,8 @@ import { AddonModWorkshopIndexLinkHandler } from './services/handlers/index-link
|
|||
import { AddonModWorkshopListLinkHandler } from './services/handlers/list-link';
|
||||
import { AddonModWorkshopModuleHandler } from './services/handlers/module';
|
||||
import { ADDON_MOD_WORKSHOP_COMPONENT, ADDON_MOD_WORKSHOP_PAGE_NAME } from '@addons/mod/workshop/constants';
|
||||
import { AddonModWorkshopPrefetchHandler } from '@addons/mod/workshop/services/handlers/prefetch-lazy';
|
||||
import { AddonModWorkshopSyncCronHandler } from '@addons/mod/workshop/services/handlers/sync-cron-lazy';
|
||||
import { getPrefetchHandlerInstance } from '@addons/mod/workshop/services/handlers/prefetch';
|
||||
import { getCronHandlerInstance } from '@addons/mod/workshop/services/handlers/sync-cron';
|
||||
|
||||
/**
|
||||
* Get workshop services.
|
||||
|
@ -85,13 +85,10 @@ const routes: Routes = [
|
|||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useValue: () => {
|
||||
// TODO use async instances
|
||||
// CoreCourseModulePrefetchDelegate.registerHandler(getPrefetchHandlerInstance());
|
||||
// CoreCronDelegate.register(getCronHandlerInstance());
|
||||
CoreCourseModulePrefetchDelegate.registerHandler(getPrefetchHandlerInstance());
|
||||
CoreCronDelegate.register(getCronHandlerInstance());
|
||||
|
||||
CoreCourseModuleDelegate.registerHandler(AddonModWorkshopModuleHandler.instance);
|
||||
CoreCourseModulePrefetchDelegate.registerHandler(AddonModWorkshopPrefetchHandler.instance);
|
||||
CoreCronDelegate.register(AddonModWorkshopSyncCronHandler.instance);
|
||||
CoreContentLinksDelegate.registerHandler(AddonModWorkshopIndexLinkHandler.instance);
|
||||
CoreContentLinksDelegate.registerHandler(AddonModWorkshopListLinkHandler.instance);
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { TupleMatches } from '@/core/utils/types';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
|
||||
/**
|
||||
|
@ -27,12 +28,16 @@ function createAsyncInstanceWrapper<
|
|||
lazyConstructor?: () => TLazyInstance | Promise<TLazyInstance>,
|
||||
): AsyncInstanceWrapper<TLazyInstance, TEagerInstance> {
|
||||
let promisedInstance: CorePromisedValue<TLazyInstance> | null = null;
|
||||
let lazyInstanceMethods: Array<string | symbol>;
|
||||
let eagerInstance: TEagerInstance;
|
||||
|
||||
return {
|
||||
get instance() {
|
||||
return promisedInstance?.value ?? undefined;
|
||||
},
|
||||
get lazyInstanceMethods() {
|
||||
return lazyInstanceMethods;
|
||||
},
|
||||
get eagerInstance() {
|
||||
return eagerInstance;
|
||||
},
|
||||
|
@ -63,6 +68,9 @@ function createAsyncInstanceWrapper<
|
|||
|
||||
promisedInstance.resolve(instance);
|
||||
},
|
||||
setLazyInstanceMethods(methods) {
|
||||
lazyInstanceMethods = methods;
|
||||
},
|
||||
setEagerInstance(instance) {
|
||||
eagerInstance = instance;
|
||||
},
|
||||
|
@ -108,10 +116,14 @@ export interface AsyncInstanceWrapper<
|
|||
TEagerInstance extends AsyncObject = Partial<TLazyInstance>
|
||||
> {
|
||||
instance?: TLazyInstance;
|
||||
lazyInstanceMethods?: Array<string | symbol>;
|
||||
eagerInstance?: TEagerInstance;
|
||||
getInstance(): Promise<TLazyInstance>;
|
||||
getProperty<P extends keyof TLazyInstance>(property: P): Promise<TLazyInstance[P]>;
|
||||
setInstance(instance: TLazyInstance): void;
|
||||
setLazyInstanceMethods<const T extends Array<string | symbol>>(
|
||||
methods: LazyMethodsGuard<T, TLazyInstance, TEagerInstance>,
|
||||
): void;
|
||||
setEagerInstance(eagerInstance: TEagerInstance): void;
|
||||
setLazyConstructor(lazyConstructor: () => TLazyInstance | Promise<TLazyInstance>): void;
|
||||
resetInstance(): void;
|
||||
|
@ -141,6 +153,12 @@ export type AsyncInstance<TLazyInstance extends TEagerInstance, TEagerInstance e
|
|||
[k in keyof TLazyInstance]: AsyncMethod<TLazyInstance[k]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Guard type to make sure that lazy methods match what the lazy class implements.
|
||||
*/
|
||||
export type LazyMethodsGuard<TMethods extends Array<string | symbol>, TLazyInstance, TEagerInstance> =
|
||||
TupleMatches<TMethods, Exclude<keyof TLazyInstance, keyof TEagerInstance>> extends true ? TMethods : never;
|
||||
|
||||
/**
|
||||
* Create an asynchronous instance proxy, where all methods will be callable directly but will become asynchronous. If the
|
||||
* underlying instance hasn't been set, methods will be resolved once it is.
|
||||
|
@ -171,6 +189,10 @@ export function asyncInstance<TLazyInstance extends TEagerInstance, TEagerInstan
|
|||
return Reflect.get(wrapper.eagerInstance, property, receiver);
|
||||
}
|
||||
|
||||
if (wrapper.lazyInstanceMethods && !wrapper.lazyInstanceMethods.includes(property)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return async (...args: unknown[]) => {
|
||||
const instance = await wrapper.getInstance();
|
||||
const method = Reflect.get(instance, property, receiver);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance';
|
||||
import { AsyncInstance, LazyMethodsGuard, asyncInstance } from '@/core/utils/async-instance';
|
||||
import { expectAnyType, expectSameTypes } from '@/testing/utils';
|
||||
|
||||
describe('AsyncInstance', () => {
|
||||
|
@ -33,8 +33,45 @@ describe('AsyncInstance', () => {
|
|||
expect(asyncService.instance).toBeUndefined();
|
||||
expect(asyncService.answer).toEqual(42);
|
||||
expect(asyncService.instance).toBeUndefined();
|
||||
expect(await asyncService.isEager()).toBe(true);
|
||||
expect(await asyncService.hello()).toEqual('Hi there!');
|
||||
expect(asyncService.instance).toBeInstanceOf(LazyService);
|
||||
expect(await asyncService.isEager()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not return undefined methods when they are declared', async () => {
|
||||
const asyncService = asyncInstance<LazyService, EagerService>(() => new LazyService());
|
||||
|
||||
asyncService.setEagerInstance(new EagerService());
|
||||
asyncService.setLazyInstanceMethods(['hello', 'goodbye']);
|
||||
|
||||
expect(asyncService.hello).not.toBeUndefined();
|
||||
expect(asyncService.goodbye).not.toBeUndefined();
|
||||
expect(asyncService.isEager).not.toBeUndefined();
|
||||
expect(asyncService.notImplemented).toBeUndefined();
|
||||
});
|
||||
|
||||
it('guards against missing or invalid instance methods', () => {
|
||||
// Define interfaces.
|
||||
interface Eager {
|
||||
lorem(): void;
|
||||
ipsum(): void;
|
||||
}
|
||||
|
||||
interface Lazy extends Eager {
|
||||
foo(): void;
|
||||
bar(): void;
|
||||
}
|
||||
|
||||
// Test valid method tuples.
|
||||
expectSameTypes<LazyMethodsGuard<['foo', 'bar'], Lazy, Eager>, ['foo', 'bar']>(true);
|
||||
expectSameTypes<LazyMethodsGuard<['bar', 'foo'], Lazy, Eager>, ['bar', 'foo']>(true);
|
||||
expectSameTypes<LazyMethodsGuard<['foo', 'foo', 'bar'], Lazy, Eager>, ['foo', 'foo', 'bar']>(true);
|
||||
|
||||
// Test invalid method tuples.
|
||||
expectSameTypes<LazyMethodsGuard<['foo'], Lazy, Eager>, never>(true);
|
||||
expectSameTypes<LazyMethodsGuard<['foo', 'bar', 'lorem'], Lazy, Eager>, never>(true);
|
||||
expectSameTypes<LazyMethodsGuard<['foo', 'bar', 'baz'], Lazy, Eager>, never>(true);
|
||||
});
|
||||
|
||||
it('preserves undefined properties after initialization', async () => {
|
||||
|
@ -73,6 +110,12 @@ class EagerService {
|
|||
|
||||
answer = 42;
|
||||
|
||||
notImplemented?(): void;
|
||||
|
||||
async isEager(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FakeEagerService {
|
||||
|
@ -83,6 +126,10 @@ class FakeEagerService {
|
|||
|
||||
class LazyService extends EagerService {
|
||||
|
||||
async isEager(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
hello(): string {
|
||||
return 'Hi there!';
|
||||
}
|
||||
|
|
|
@ -39,6 +39,47 @@ export type Pretty<T> = T extends infer U ? {[K in keyof U]: U[K]} : never;
|
|||
*/
|
||||
export type SubPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
/**
|
||||
* Helper type to negate a boolean type.
|
||||
*/
|
||||
export type Not<T extends boolean> = IsTrue<T> extends true ? false : (IsFalse<T> extends true ? true : boolean);
|
||||
|
||||
/**
|
||||
* Helper type to check whether a boolean type is exactly `true`.
|
||||
*/
|
||||
export type IsTrue<T extends boolean> = Exclude<T, true> extends never ? true : false;
|
||||
|
||||
/**
|
||||
* Helper type to check whether a boolean type is exactly `false`.
|
||||
*/
|
||||
export type IsFalse<T extends boolean> = Exclude<T, false> extends never ? true : false;
|
||||
|
||||
/**
|
||||
* Helper type to check whether the given tuple contains all the items in a union.
|
||||
*/
|
||||
export type TupleContainsAll<TTuple extends unknown[], TItems> = Exclude<
|
||||
TItems,
|
||||
TTuple[number]
|
||||
> extends never ? true : false;
|
||||
|
||||
/**
|
||||
* Helper type to check whether the given tuple contains any items outside of a union.
|
||||
*/
|
||||
export type TupleContainsOthers<TTuple extends unknown[], TItems> = Exclude<
|
||||
TTuple[number],
|
||||
TItems
|
||||
> extends never ? false : true;
|
||||
|
||||
/**
|
||||
* Helper type to check whether the given tuple matches the items in a union.
|
||||
*
|
||||
* This means that the tuple will have all the items from the union, but not any outside of it.
|
||||
*/
|
||||
export type TupleMatches<TTuple extends unknown[], TItems> = IsTrue<
|
||||
TupleContainsAll<TTuple, TItems> |
|
||||
Not<TupleContainsOthers<TTuple, TItems>>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Helper type to omit union.
|
||||
* You can use it if need to omit an element from types union.
|
||||
|
|
Loading…
Reference in New Issue