Skip to content

Commit 9dc00f5

Browse files
authored
Merge pull request #320 from chshanovskiy/main
2 parents d28b802 + a49a74c commit 9dc00f5

File tree

5 files changed

+160
-0
lines changed

5 files changed

+160
-0
lines changed

src/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ All methods split into categories.
3838

3939
- [combineEvents](./combine-events/readme.md) — Wait for all passed events is triggered.
4040
- [format](./format/readme.md) — Combine stores to a string literal.
41+
- [readonly](./readonly/readme.md) — Create readonly version of store or event.
4142
- [reshape](./reshape/readme.md) — Destructure one store to different stores
4243
- [snapshot](./snapshot/readme.md) — Create store value snapshot.
4344
- [splitMap](./split-map/readme.md) — Split event to different events and map data.

src/readonly/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Store, Event, is, combine } from 'effector';
2+
3+
export function readonly<T extends unknown>(source: Store<T>): Store<T>;
4+
export function readonly<T extends unknown>(source: Event<T>): Event<T>;
5+
6+
export function readonly<T extends unknown>(source: Store<T> | Event<T>) {
7+
if (!is.targetable(source)) {
8+
return source;
9+
}
10+
11+
if (is.store(source)) {
12+
return source.map((value) => value, { skipVoid: false });
13+
}
14+
15+
if (is.event(source)) {
16+
return source.map((value) => value);
17+
}
18+
19+
return source;
20+
}

src/readonly/readme.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# readonly
2+
3+
:::note since
4+
patronum 2.2.0
5+
:::
6+
7+
```ts
8+
import { readonly } from 'patronum';
9+
// or
10+
import { readonly } from 'patronum/readonly';
11+
```
12+
13+
### Motivation
14+
15+
The method allows to convert writable store and callable event to their readonly versions.
16+
It can be helpful to create more strict public api.
17+
18+
### Formulae
19+
20+
```ts
21+
$result = readonly($store);
22+
```
23+
24+
- `$result` store contains mapped `$store`, which is readonly for consumers.
25+
26+
```ts
27+
result = readonly(event);
28+
```
29+
30+
- `result` event contains mapped `event`, which is not callable by consumers.
31+
32+
### Arguments
33+
34+
- `value: Store<T>|Event<T>` — Any store or event, that required to be readonly
35+
36+
### Returns
37+
38+
- `result: Store<T>|Event<T>`
39+
40+
Note: if passed argument is already derived, then argument returns as-is.
41+
42+
### Example
43+
44+
```ts
45+
const $store = createStore({});
46+
const $readonlyStore = readonly($store);
47+
48+
console.assert(false === is.targetable($readonlyStore));
49+
```
50+
51+
```ts
52+
const event = createEvent();
53+
const readonlyEvent = readonly(event);
54+
55+
console.assert(false === is.targetable($readonlyStore));
56+
```

src/readonly/readonly.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { createEvent, createStore, is } from 'effector';
2+
import { readonly } from './index';
3+
4+
it('should convert store to readonly store', () => {
5+
const $store = createStore({});
6+
const $result = readonly($store);
7+
8+
expect(is.targetable($result)).toBe(false);
9+
});
10+
11+
it('should convert event to readonly event', () => {
12+
const event = createEvent();
13+
const result = readonly(event);
14+
15+
expect(is.targetable(result)).toBe(false);
16+
});
17+
18+
it('should return store as-is if it is already derived', () => {
19+
const $store = createStore({});
20+
const $mapped = $store.map((state) => state);
21+
const $result = readonly($mapped);
22+
23+
expect($result).toBe($mapped);
24+
});
25+
26+
it('should return event as-is if it is already derived', () => {
27+
const event = createEvent();
28+
const mapped = event.map((value) => value);
29+
const result = readonly(mapped);
30+
31+
expect(result).toBe(mapped);
32+
});

test-typings/readonly.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { createDomain, createEffect, createEvent, createStore, Store, Event } from 'effector';
2+
import { expectType } from 'tsd';
3+
import { readonly } from '../dist/readonly';
4+
5+
// Always returns the store
6+
{
7+
const $store = createStore<number>(1);
8+
9+
expectType<Store<number>>(readonly($store));
10+
}
11+
12+
// Always returns the store
13+
{
14+
const $store = createStore<number>(1);
15+
const $mapped = $store.map(store => store)
16+
17+
expectType<Store<number>>(readonly($mapped));
18+
}
19+
20+
// Always returns the event
21+
{
22+
const event = createEvent<void>();
23+
24+
expectType<Event<void>>(readonly(event));
25+
}
26+
27+
// Always returns the event
28+
{
29+
const event = createEvent<void>();
30+
const mapped = event.map(event => event)
31+
32+
expectType<Event<void>>(readonly(mapped));
33+
}
34+
35+
// Should not receive non-store or non-event as argument
36+
{
37+
// @ts-expect-error
38+
readonly(createEffect());
39+
40+
// @ts-expect-error
41+
readonly(createDomain());
42+
43+
// @ts-expect-error
44+
readonly(1);
45+
46+
// @ts-expect-error
47+
readonly(true);
48+
49+
// @ts-expect-error
50+
readonly({});
51+
}

0 commit comments

Comments
 (0)