From f8d69a83e24b596e682cb4b117e156b488340a94 Mon Sep 17 00:00:00 2001 From: waiting <1661926154@qq.com> Date: Sun, 28 Jul 2024 10:32:56 +0800 Subject: [PATCH] feat(cache): change this of decorator methods from WebContext to the class decorated remove validation of WebContext --- .../src/lib/cacheable/cacheable.handler.ts | 6 +- .../src/lib/cacheable/cacheable.helper.ts | 7 +- .../src/lib/cacheevict/cacheevict.handler.ts | 6 +- .../src/lib/cacheevict/cacheevict.helper.ts | 10 +- .../src/lib/cacheput/cacheput.handler.ts | 6 +- .../cache/src/lib/cacheput/cacheput.helper.ts | 10 +- packages/cache/src/lib/helper.ts | 55 ++---- packages/cache/src/lib/types.ts | 39 ++-- packages/cache/test/api-test.ts | 1 + packages/cache/test/cacheable/25.this.test.ts | 47 +++++ .../src/cacheable/25a.this.controller.ts | 175 ++++++++++++++++++ .../src/cacheable/25b.this.service.ts | 60 ++++++ 12 files changed, 355 insertions(+), 67 deletions(-) create mode 100644 packages/cache/test/cacheable/25.this.test.ts create mode 100644 packages/cache/test/fixtures/base-app/src/cacheable/25a.this.controller.ts create mode 100644 packages/cache/test/fixtures/base-app/src/cacheable/25b.this.service.ts diff --git a/packages/cache/src/lib/cacheable/cacheable.handler.ts b/packages/cache/src/lib/cacheable/cacheable.handler.ts index 80e82a58..f3f95db5 100644 --- a/packages/cache/src/lib/cacheable/cacheable.handler.ts +++ b/packages/cache/src/lib/cacheable/cacheable.handler.ts @@ -1,4 +1,5 @@ -import { Singleton } from '@midwayjs/core' +import { Singleton, Inject } from '@midwayjs/core' +import { TraceService } from '@mwcp/otel' import { DecoratorExecutorParamBase } from '@mwcp/share' import { DecoratorHandlerCacheBase } from '../decorator.handler.types.js' @@ -13,11 +14,14 @@ import { before, around } from './cacheable.helper.js' */ @Singleton() export class DecoratorHandlerCacheable extends DecoratorHandlerCacheBase { + @Inject() readonly traceService: TraceService + override genExecutorParam(options: DecoratorExecutorParamBase): DecoratorExecutorOptions { const optsExt: GenDecoratorExecutorOptionsExt = { config: this.cacheConfig, cachingFactory: this.cachingFactory, op: 'cacheable', + traceService: this.traceService, } const ret = genDecoratorExecutorOptions(options, optsExt) return ret diff --git a/packages/cache/src/lib/cacheable/cacheable.helper.ts b/packages/cache/src/lib/cacheable/cacheable.helper.ts index 8608bbd3..4f5967d6 100644 --- a/packages/cache/src/lib/cacheable/cacheable.helper.ts +++ b/packages/cache/src/lib/cacheable/cacheable.helper.ts @@ -18,12 +18,11 @@ import type { CachedResponse, CacheableArgs, DecoratorExecutorOptions } from '.. export async function before(options: DecoratorExecutorOptions>): Promise { const { - webContext, mergedDecoratorParam, cachingFactory, cachingInstanceId, + instance, } = options - assert(webContext, 'webContext is undefined') const mergedDecoratorParam2: CacheableArgs = { ...initCacheableArgs, @@ -34,7 +33,7 @@ export async function before(options: DecoratorExecutorOptions>): Promise { const { - webContext, cacheKey, } = options - assert(webContext, 'webContext is undefined') if (options.methodResult) { return options.methodResult diff --git a/packages/cache/src/lib/cacheevict/cacheevict.handler.ts b/packages/cache/src/lib/cacheevict/cacheevict.handler.ts index f06fe23e..989e5fe8 100644 --- a/packages/cache/src/lib/cacheevict/cacheevict.handler.ts +++ b/packages/cache/src/lib/cacheevict/cacheevict.handler.ts @@ -1,4 +1,5 @@ -import { Singleton } from '@midwayjs/core' +import { Singleton, Inject } from '@midwayjs/core' +import { TraceService } from '@mwcp/otel' import type { DecoratorExecutorParamBase } from '@mwcp/share' import { DecoratorHandlerCacheBase } from '../decorator.handler.types.js' @@ -14,11 +15,14 @@ import { around, before } from './cacheevict.helper.js' */ @Singleton() export class DecoratorHandlerCacheEvict extends DecoratorHandlerCacheBase { + @Inject() readonly traceService: TraceService + override genExecutorParam(options: DecoratorExecutorParamBase): DecoratorExecutorOptions { const optsExt: GenDecoratorExecutorOptionsExt = { config: this.cacheConfig, cachingFactory: this.cachingFactory, op: 'cacheevict', + traceService: this.traceService, } const ret = genDecoratorExecutorOptions(options, optsExt) return ret diff --git a/packages/cache/src/lib/cacheevict/cacheevict.helper.ts b/packages/cache/src/lib/cacheevict/cacheevict.helper.ts index 4b340dca..caa2cc96 100644 --- a/packages/cache/src/lib/cacheevict/cacheevict.helper.ts +++ b/packages/cache/src/lib/cacheevict/cacheevict.helper.ts @@ -8,10 +8,9 @@ import type { CacheEvictArgs, DecoratorExecutorOptions } from '../types.js' export async function before(options: DecoratorExecutorOptions): Promise { const { - webContext, + instance, mergedDecoratorParam, } = options - assert(webContext, 'webContext is undefined') const mergedDecoratorParam2: CacheEvictArgs = { ...initCacheEvictArgs, @@ -21,7 +20,7 @@ export async function before(options: DecoratorExecutorOptions): ...mergedDecoratorParam2, methodArgs: options.methodArgs, methodResult: void 0, - webContext, + instance, } const cacheKey = genCacheKey(opts2) options.mergedDecoratorParam = mergedDecoratorParam2 @@ -30,13 +29,12 @@ export async function before(options: DecoratorExecutorOptions): export async function around(options: DecoratorExecutorOptions): Promise { const { - webContext, + instance, cachingFactory, cachingInstanceId, cacheKey, mergedDecoratorParam, } = options - assert(webContext, 'webContext is undefined') assert(mergedDecoratorParam, 'mergedDecoratorParam is undefined') const tmp = computerWriteConditionValue(options) @@ -72,7 +70,7 @@ export async function around(options: DecoratorExecutorOptions): ...options.mergedDecoratorParam as CacheEvictArgs, methodArgs: options.methodArgs, methodResult: options.methodResult, - webContext, + instance, } const cacheKey2 = genCacheKey(opts2) cacheKey2 && await deleteData(caching, cacheKey2) diff --git a/packages/cache/src/lib/cacheput/cacheput.handler.ts b/packages/cache/src/lib/cacheput/cacheput.handler.ts index fc976ca1..8aba00ac 100644 --- a/packages/cache/src/lib/cacheput/cacheput.handler.ts +++ b/packages/cache/src/lib/cacheput/cacheput.handler.ts @@ -1,4 +1,5 @@ -import { Singleton } from '@midwayjs/core' +import { Singleton, Inject } from '@midwayjs/core' +import { TraceService } from '@mwcp/otel' import { DecoratorExecutorParamBase } from '@mwcp/share' import { DecoratorHandlerCacheBase } from '../decorator.handler.types.js' @@ -14,11 +15,14 @@ import { before, decoratorExecutor } from './cacheput.helper.js' */ @Singleton() export class DecoratorHandlerCachePut extends DecoratorHandlerCacheBase { + @Inject() readonly traceService: TraceService + override genExecutorParam(options: DecoratorExecutorParamBase): DecoratorExecutorOptions { const optsExt: GenDecoratorExecutorOptionsExt = { config: this.cacheConfig, cachingFactory: this.cachingFactory, op: 'cacheput', + traceService: this.traceService, } const ret = genDecoratorExecutorOptions(options, optsExt) return ret diff --git a/packages/cache/src/lib/cacheput/cacheput.helper.ts b/packages/cache/src/lib/cacheput/cacheput.helper.ts index 867105ba..fd786b8a 100644 --- a/packages/cache/src/lib/cacheput/cacheput.helper.ts +++ b/packages/cache/src/lib/cacheput/cacheput.helper.ts @@ -15,8 +15,7 @@ import type { CacheableArgs, CachedResponse, DecoratorExecutorOptions } from '.. export async function before(options: DecoratorExecutorOptions>): Promise { - const { webContext, mergedDecoratorParam } = options - assert(webContext, 'webContext is undefined') + const { instance, mergedDecoratorParam } = options const mergedDecoratorParam2: CacheableArgs = { ...initCachePutArgs, @@ -28,7 +27,7 @@ export async function before(options: DecoratorExecutorOptions): Promise { const { - webContext, + instance, cachingFactory, cachingInstanceId, cacheKey, } = options - assert(webContext, 'webContext is undefined') const { method, methodArgs } = options assert(method, 'original method invalid') @@ -75,7 +73,7 @@ export async function decoratorExecutor(options: DecoratorExecutorOptions( export function computerWriteConditionValue(options: DecoratorExecutorOptions): boolean | Promise { - const { mergedDecoratorParam: cacheOptions } = options + const { mergedDecoratorParam: cacheOptions, instance } = options assert(cacheOptions, 'cacheOptions is undefined within computerConditionValue()') - const webContext = options.instance[REQUEST_OBJ_CTX_KEY] - assert(webContext, 'webContext is undefined') - switch (typeof cacheOptions.writeCondition) { case 'undefined': return true @@ -260,7 +256,7 @@ export function computerWriteConditionValue(options: DecoratorExecutorOptions boolean | Promise return fn.call( - webContext, + instance, options.methodArgs, options.methodResult, ) @@ -272,12 +268,9 @@ export function computerWriteConditionValue(options: DecoratorExecutorOptions): boolean | Promise { - const { mergedDecoratorParam: cacheOptions } = options + const { mergedDecoratorParam: cacheOptions, instance } = options assert(cacheOptions, 'cacheOptions is undefined within computerReadConditionValue()') - const webContext = options.instance[REQUEST_OBJ_CTX_KEY] - assert(webContext, 'webContext is undefined') - switch (typeof cacheOptions.condition) { case 'undefined': return true @@ -288,7 +281,7 @@ export function computerReadConditionValue(options: DecoratorExecutorOptions boolean | Promise return fn.call( - webContext, + instance, options.methodArgs, options.methodResult, ) @@ -304,12 +297,9 @@ export function computerTTLValue( options: DecoratorExecutorOptions, ): number | Promise { - const { mergedDecoratorParam: cacheOptions } = options + const { mergedDecoratorParam: cacheOptions, instance } = options assert(cacheOptions, 'cacheOptions is undefined within computerTTLValue()') - const webContext = options.instance[REQUEST_OBJ_CTX_KEY] - assert(webContext, 'webContext is undefined') - let ttl = 10 // second switch (typeof cacheOptions.ttl) { @@ -325,7 +315,7 @@ export function computerTTLValue( // eslint-disable-next-line @typescript-eslint/no-explicit-any const fn = cacheOptions.ttl as CacheTTLFn return fn.call( - webContext, + instance, options.methodArgs, result, ) @@ -380,16 +370,11 @@ export function genDecoratorExecutorOptions( cacheOptions.cacheName = cacheName } - let traceService - if (optionsBase.webContext) { - traceService = optionsBase.webContext[`_${OtelConfigKey.serviceName}`] as AbstractTraceService | undefined - assert(traceService, 'TraceService is not initialized. (TraceService 尚未初始化)') - } + assert(optionsExt.traceService, 'genDecoratorExecutorOptions(): traceService is undefined') const ret: DecoratorExecutorOptions = { ...optionsBase, ...optionsExt, - traceService, mergedDecoratorParam: cacheOptions, } return ret diff --git a/packages/cache/src/lib/types.ts b/packages/cache/src/lib/types.ts index a8d7a311..6ce88aa2 100644 --- a/packages/cache/src/lib/types.ts +++ b/packages/cache/src/lib/types.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { CachingFactory, CacheManagerOptions } from '@midwayjs/cache-manager' import type { AbstractTraceService } from '@mwcp/otel' -import type { Context, DecoratorExecutorParamBase } from '@mwcp/share' +import type { DecoratorExecutorParamBase, ClzInstance } from '@mwcp/share' import type { MethodType, MiddlewareConfig as MWConfig } from '@waiting/shared-types' @@ -39,8 +39,11 @@ export interface Config { * @param result Result of the method. Only for using `@CacheEvict` * @returns if undefined, there is no tailing ":" in cacheName */ -export type KeyGenerator = ( - this: Context, +export type KeyGenerator< + M extends MethodType | undefined = undefined, + MThis = unknown extends ThisParameterType ? ClzInstance : ThisParameterType, +> = ( + this: MThis, /** Arguments of the method */ args: M extends MethodType ? Parameters : any, /** @@ -50,21 +53,30 @@ export type KeyGenerator = ( result: M extends MethodType ? Awaited> : undefined ) => string | undefined | false // undefined/false means skip cache -export type WriteCacheConditionFn = ( - this: Context, +export type WriteCacheConditionFn< + M extends MethodType | undefined = undefined, + MThis = unknown extends ThisParameterType ? ClzInstance : ThisParameterType, +> = ( + this: MThis, /** Arguments of the method */ args: M extends MethodType ? Parameters : any, result: M extends MethodType ? Awaited> : any ) => boolean | Promise -export type ReadCacheConditionFn = ( - this: Context, +export type ReadCacheConditionFn< + M extends MethodType | undefined = undefined, + MThis = unknown extends ThisParameterType ? ClzInstance : ThisParameterType, +> = ( + this: MThis, /** Arguments of the method */ args: M extends MethodType ? Parameters : any, ) => boolean | Promise -export type EvictCacheConditionFn = ( - this: Context, +export type EvictCacheConditionFn< + M extends MethodType | undefined = undefined, + MThis = unknown extends ThisParameterType ? ClzInstance : ThisParameterType, +> = ( + this: MThis, /** Arguments of the method */ args: M extends MethodType ? Parameters : any, /** @@ -75,8 +87,11 @@ export type EvictCacheConditionFn result: M extends MethodType ? (Awaited> | undefined) : any ) => boolean | Promise -export type CacheTTLFn = ( - this: Context, +export type CacheTTLFn< + M extends MethodType | undefined = undefined, + MThis = unknown extends ThisParameterType ? ClzInstance : ThisParameterType, +> = ( + this: MThis, /** Arguments of the method */ args: M extends MethodType ? Parameters : any, /** @@ -171,7 +186,6 @@ export interface CacheEvictArgs { export type DecoratorExecutorOptions = DecoratorExecutorParamBase & GenDecoratorExecutorOptionsExt & { - traceService: AbstractTraceService | undefined, /** * @default 'default' */ @@ -183,5 +197,6 @@ export interface GenDecoratorExecutorOptionsExt { config: Config cachingFactory: CachingFactory op: 'cacheable' | 'cacheput' | 'cacheevict' + traceService: AbstractTraceService | undefined } diff --git a/packages/cache/test/api-test.ts b/packages/cache/test/api-test.ts index a0f826c7..67933d7a 100644 --- a/packages/cache/test/api-test.ts +++ b/packages/cache/test/api-test.ts @@ -8,6 +8,7 @@ export const apiBase = { args: '/args', class_cacheable: '/class_cacheable', + this_cacheable: '/this_cacheable', key_generator: '/key_generator', method_cacheable: '/method_cacheable', method_cacheevict: '/method_cacheevict', diff --git a/packages/cache/test/cacheable/25.this.test.ts b/packages/cache/test/cacheable/25.this.test.ts new file mode 100644 index 00000000..6639917f --- /dev/null +++ b/packages/cache/test/cacheable/25.this.test.ts @@ -0,0 +1,47 @@ +import assert from 'node:assert/strict' + +import { fileShortPath } from '@waiting/shared-core' + +import { apiBase, apiMethod } from '#@/api-test.js' +import { testConfig } from '#@/root.config.js' + + +describe(fileShortPath(import.meta.url), function () { + describe('Should @Cacheable decorator work', () => { + const prefix = apiBase.this_cacheable + + it(apiMethod.simple, async () => { + const { httpRequest } = testConfig + const url = `${prefix}/${apiMethod.simple}` + + const resp = await httpRequest.get(url) + assert(resp.ok, resp.text) + }) + + it(apiMethod.method_override_class, async () => { + const { httpRequest } = testConfig + const url = `${prefix}/${apiMethod.method_override_class}` + + const resp = await httpRequest.get(url) + assert(resp.ok, resp.text) + }) + + + it(apiMethod.ttlFn, async () => { + const { httpRequest } = testConfig + const url = `${prefix}/${apiMethod.ttlFn}` + + const resp = await httpRequest.get(url) + assert(resp.ok, resp.text) + }) + + it(apiMethod.ttl_fn2, async () => { + const { httpRequest } = testConfig + const url = `${prefix}/${apiMethod.ttl_fn2}` + + const resp = await httpRequest.get(url) + assert(resp.ok, resp.text) + }) + }) +}) + diff --git a/packages/cache/test/fixtures/base-app/src/cacheable/25a.this.controller.ts b/packages/cache/test/fixtures/base-app/src/cacheable/25a.this.controller.ts new file mode 100644 index 00000000..9db135e7 --- /dev/null +++ b/packages/cache/test/fixtures/base-app/src/cacheable/25a.this.controller.ts @@ -0,0 +1,175 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import assert from 'node:assert/strict' + +import { CachingFactory, MidwayCache, SingleCacheOptions } from '@midwayjs/cache-manager' +import { + Controller, + Get, + Init, + Inject, + InjectClient, +} from '@midwayjs/core' +import { MConfig } from '@mwcp/share' +import { sleep } from '@waiting/shared-core' + +import { validateMeta } from '../base.helper.js' +import { apiBase, apiMethod } from '../types/api-test.js' +import { Config, ConfigKey } from '../types/lib-types.js' + +import { ThisService as ClassDecoratorService } from './25b.this.service.js' + + +@Controller(apiBase.this_cacheable) +export class ThisController { + + @MConfig(ConfigKey.config) readonly cacheManagerConfig: Config + + @InjectClient(CachingFactory, 'default') cache: MidwayCache + + @Inject() svc: ClassDecoratorService + + readonly controllerName = 'ThisService' + + private midwayConfig: { ttl: number } // MidwayConfig + + @Init() + async init() { + const defaultConfig = this.cacheManagerConfig.clients['default'] as SingleCacheOptions + assert(defaultConfig) + // @ts-expect-error + const configOpt = defaultConfig.options as { ttl: number } // MidwayConfig + assert(configOpt) + this.midwayConfig = configOpt + } + + @Get(`/${apiMethod.simple}`) + async simple(): Promise<'OK'> { + const cacheKey = `${this.controllerName}.simple` + + const ret = await this.svc.simple() + assert(ret.value === 'OK') + assert(! ret[ConfigKey.CacheMetaType]) + + const ret2 = await this.svc.simple() + validateMeta(ret2, cacheKey, this.midwayConfig.ttl) + + await sleep(this.svc.ttlValue * 1001) + + const ret2a = await this.svc.simple() + validateMeta(ret2a, cacheKey, this.midwayConfig.ttl) + + return 'OK' + } + + @Get(`/${apiMethod.method_override_class}`) + async argsOverride(): Promise<'OK'> { + const cacheKey = `${this.controllerName}.ttl` + + const ret = await this.svc.ttl() + assert(ret.value === 'OK') + assert(! ret[ConfigKey.CacheMetaType]) + + const ret2 = await this.svc.ttl() + validateMeta(ret2, cacheKey, this.svc.ttlValue) + + await sleep(this.svc.ttlValue * 1001) + + const ret2a = await this.svc.ttl() + assert(! ret2a[ConfigKey.CacheMetaType]) + + return 'OK' + } + + @Get(`/${apiMethod.ttlFn}`) + async ttlFn(): Promise<'OK'> { + + const cacheKey = `${this.controllerName}.ttlFn` + + const ret = await this.svc.ttlFn('fake') + assert(ret.value === 'OK') + assert(! ret[ConfigKey.CacheMetaType]) + + const ret2 = await this.svc.ttlFn('fake') + assert(ret2.value === 'OK') + assert(! ret2[ConfigKey.CacheMetaType]) + + const ret3 = await this.svc.ttlFn('foo') + assert(ret3.value === 'OK') + assert(! ret3[ConfigKey.CacheMetaType]) + + const ret3a = await this.svc.ttlFn('foo') + assert(ret3a.value === 'OK') + validateMeta(ret3a, `${cacheKey}:foo`, this.svc.ttlValue) + + await sleep(this.svc.ttlValue * 1001) + + const ret4 = await this.svc.ttlFn('foo') + assert(ret4.value === 'OK') + assert(! ret4[ConfigKey.CacheMetaType]) + + const ret4a = await this.svc.ttlFn('foo') + assert(ret4a.value === 'OK') + validateMeta(ret4a, `${cacheKey}:foo`, this.svc.ttlValue) + + return 'OK' + } + + @Get(`/${apiMethod.ttl_fn2}`) + async ttlFn2(): Promise<'OK'> { + const cacheKey = `${this.controllerName}.ttlFn2` + + const ret5 = await this.svc.ttlFn2('fake') + assert(Number.isNaN(ret5.value)) + assert(! ret5[ConfigKey.CacheMetaType]) + + const ret5a = await this.svc.ttlFn2('') + assert(ret5a.value === 0) + assert(! ret5a[ConfigKey.CacheMetaType]) + + const ret5b = await this.svc.ttlFn2('') + assert(ret5b.value === 0) + assert(! ret5b[ConfigKey.CacheMetaType]) + + const ret6 = await this.svc.ttlFn2('-1') + assert(ret6.value === -1) + assert(! ret6[ConfigKey.CacheMetaType]) + + const ret6a = await this.svc.ttlFn2('-1') + assert(ret6a.value === -1) + assert(! ret6[ConfigKey.CacheMetaType]) + + const ret7 = await this.svc.ttlFn2('0') + assert(ret7.value === 0) + assert(! ret7[ConfigKey.CacheMetaType]) + + const ret7a = await this.svc.ttlFn2('0') + assert(ret7a.value === 0) + assert(! ret7[ConfigKey.CacheMetaType]) + + const ret8 = await this.svc.ttlFn2('1') + assert(ret8.value === 1) + assert(! ret8[ConfigKey.CacheMetaType]) + + const ret8a = await this.svc.ttlFn2('1') + assert(ret8a.value === 1) + validateMeta(ret8a, `${cacheKey}:1`, 1) + + await sleep(this.svc.ttlValue * 1001) + + const ret9 = await this.svc.ttlFn2('1') + assert(ret9.value === 1) + assert(! ret9[ConfigKey.CacheMetaType]) + + const ret9b = await this.svc.ttlFn2('1') + assert(ret9b.value === 1) + validateMeta(ret9b, `${cacheKey}:1`, 1) + + const ret10 = await this.svc.ttlFn2('2') + assert(ret10.value === 2) + assert(! ret10[ConfigKey.CacheMetaType]) + + return 'OK' + } + +} + diff --git a/packages/cache/test/fixtures/base-app/src/cacheable/25b.this.service.ts b/packages/cache/test/fixtures/base-app/src/cacheable/25b.this.service.ts new file mode 100644 index 00000000..64544cbb --- /dev/null +++ b/packages/cache/test/fixtures/base-app/src/cacheable/25b.this.service.ts @@ -0,0 +1,60 @@ +import assert from 'assert' + +import { Singleton } from '@midwayjs/core' + +import { Cacheable } from '../types/index.js' +import { CachedResponse } from '../types/lib-types.js' + + +const cacheName = 'foo' + +@Cacheable() +@Singleton() +export class ThisService { + readonly ttlValue = 1 + + async simple(): Promise> { + return { value: 'OK' } + } + + @Cacheable({ + ttl() { + assert(typeof this === 'object', 'this must be object') + assert(typeof this.ttlValue === 'number', 'this.ttlValue must be number') + return this.ttlValue + }, + }) + async ttl(this: ThisService): Promise> { + return { value: 'OK' } + } + + @Cacheable({ + ttl([input]) { + return input === 'foo' ? this.ttlValue : 0 + }, + }) + async ttlFn(this: ThisService, input: string): Promise> { + void input + return { value: 'OK' } + } + + @Cacheable({ + ttl: ([input], resp) => input.length && resp && +resp.value > 0 ? +resp.value : 0, + }) + async ttlFn2(this: ThisService, input: string): Promise> { + return { value: +input } + } + + @Cacheable({ + cacheName, + ttl() { + assert(typeof this === 'object', 'this must be object') + assert(typeof this.ttlValue === 'number', 'this.ttlValue must be number') + return this.ttlValue + }, + }) + async name(this: ThisService): Promise> { + return { value: 'OK' } + } + +}