From d703b71aafed4c4e18fcfb0fd87f267b146aa5fa Mon Sep 17 00:00:00 2001 From: Chshanovskiy Maxim Date: Sat, 13 Jan 2024 23:18:45 +0530 Subject: [PATCH] feat(readonly): implement readonly operator --- src/index.md | 1 + src/readonly/index.ts | 8 ++++++ src/readonly/readme.md | 54 +++++++++++++++++++++++++++++++++++ src/readonly/readonly.test.ts | 16 +++++++++++ test-typings/readonly.ts | 51 +++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 src/readonly/index.ts create mode 100644 src/readonly/readme.md create mode 100644 src/readonly/readonly.test.ts create mode 100644 test-typings/readonly.ts diff --git a/src/index.md b/src/index.md index a6b5a900..8e669966 100644 --- a/src/index.md +++ b/src/index.md @@ -38,6 +38,7 @@ All methods split into categories. - [combineEvents](./combine-events/readme.md) — Wait for all passed events is triggered. - [format](./format/readme.md) — Combine stores to a string literal. +- [readonly](./readonly/readme.md) — Create readonly version of store or event. - [reshape](./reshape/readme.md) — Destructure one store to different stores - [snapshot](./snapshot/readme.md) — Create store value snapshot. - [splitMap](./split-map/readme.md) — Split event to different events and map data. diff --git a/src/readonly/index.ts b/src/readonly/index.ts new file mode 100644 index 00000000..63441197 --- /dev/null +++ b/src/readonly/index.ts @@ -0,0 +1,8 @@ +import { Store, Event } from 'effector'; + +export function readonly(source: Store): Store; +export function readonly(source: Event): Event; + +export function readonly(source: Store | Event) { + return source.map((value) => value, { skipVoid: false }); +} diff --git a/src/readonly/readme.md b/src/readonly/readme.md new file mode 100644 index 00000000..54de2e1f --- /dev/null +++ b/src/readonly/readme.md @@ -0,0 +1,54 @@ +# readonly + +:::note since +patronum 2.2.0 +::: + +```ts +import { readonly } from 'patronum'; +// or +import { readonly } from 'patronum/readonly'; +``` + +### Motivation + +The method allows to convert writable store and callable event to their readonly versions. +It can be helpful to create more strict public api. + +### Formulae + +```ts +$result = readonly($store); +``` + +- `$result` store contains mapped `$store`, which is readonly for consumers. + +```ts +result = readonly(event); +``` + +- `result` event contains mapped `event`, which is not callable by consumers. + +### Arguments + +- `value: Store|Event` — Any store or event, that required to be readonly + +### Returns + +- `result: Store|Event` + +### Example + +```ts +const $store = createStore({}); +const $readonlyStore = readonly($store); + +console.assert(false === is.targetable($readonlyStore)); +``` + +```ts +const event = createEvent(); +const readonlyEvent = readonly(event); + +console.assert(false === is.targetable($readonlyStore)); +``` diff --git a/src/readonly/readonly.test.ts b/src/readonly/readonly.test.ts new file mode 100644 index 00000000..0a387b12 --- /dev/null +++ b/src/readonly/readonly.test.ts @@ -0,0 +1,16 @@ +import { createEvent, createStore, is } from 'effector'; +import { readonly } from './index'; + +it('should convert store to readonly store', () => { + const $store = createStore({}); + const $result = readonly($store); + + expect(is.targetable($result)).toBe(false); +}); + +it('should convert event to readonly event', () => { + const event = createEvent(); + const result = readonly(event); + + expect(is.targetable(result)).toBe(false); +}); diff --git a/test-typings/readonly.ts b/test-typings/readonly.ts new file mode 100644 index 00000000..d370af2c --- /dev/null +++ b/test-typings/readonly.ts @@ -0,0 +1,51 @@ +import { createDomain, createEffect, createEvent, createStore, Store, Event } from 'effector'; +import { expectType } from 'tsd'; +import { readonly } from '../dist/readonly'; + +// Always returns the store +{ + const $store = createStore(1); + + expectType>(readonly($store)); +} + +// Always returns the store +{ + const $store = createStore(1); + const $mapped = $store.map(store => store) + + expectType>(readonly($mapped)); +} + +// Always returns the event +{ + const event = createEvent(); + + expectType>(readonly(event)); +} + +// Always returns the event +{ + const event = createEvent(); + const mapped = event.map(event => event) + + expectType>(readonly(mapped)); +} + +// Should not receive non-store or non-event as argument +{ + // @ts-expect-error + readonly(createEffect()); + + // @ts-expect-error + readonly(createDomain()); + + // @ts-expect-error + readonly(1); + + // @ts-expect-error + readonly(true); + + // @ts-expect-error + readonly({}); +}