diff --git a/index.ts b/index.ts index e69de29..0e3a946 100644 --- a/index.ts +++ b/index.ts @@ -0,0 +1 @@ +export * from './lazy/index.js' diff --git a/lazy/index.ts b/lazy/index.ts new file mode 100644 index 0000000..7f47604 --- /dev/null +++ b/lazy/index.ts @@ -0,0 +1,17 @@ +/** + * A lazy function for typescript projects. + */ +export const lazy = (create: () => T) => { + let instance: T | undefined = undefined + return (): T => instance ??= create() +} + +/* + * Example: + * const config = lazy( + * () => fs.readFileSync('config.json').then(f => cfgSchema.parse(f)) + * ) + * + * config().PROP_1 + * config().PROP_2 // config file is read and parsed only once + */ diff --git a/lazy/lazy.test.ts b/lazy/lazy.test.ts new file mode 100644 index 0000000..ff42dd3 --- /dev/null +++ b/lazy/lazy.test.ts @@ -0,0 +1,45 @@ +import { expect, test } from 'vitest' +import { lazy } from './index.js' + +test('lazy remembers the value of its function', () => { + const lazyValue = lazy(() => 1) + + expect(lazyValue()).toBe(1) +}) + +test('lazy executes its function only once', () => { + let countExecutions = 0 + + const lazyValue = lazy(() => { + countExecutions++ + return 1 + }) + + lazyValue() + lazyValue() + + expect(countExecutions).toBe(1) +}) + +test('lazy does propagate exceptions on first use', () => { + const lazyValue = lazy(() => { + throw new Error() + }) + + expect(lazyValue).toThrowError() +}) + +test('lazy retries on exceptions', () => { + let countExecutions = 0 + + const lazyValue = lazy(() => { + if (countExecutions === 0) { + countExecutions++ + throw new Error() + } + return 1 + }) + + expect(lazyValue).toThrowError() + expect(lazyValue()).toBe(1) +})