1 | # typescript-memoize
|
2 |
|
3 | [](https://www.npmjs.com/package/typescript-memoize)
|
4 | [](https://raw.githubusercontent.com/darrylhodgins/typescript-memoize/master/LICENSE)
|
5 | 
|
6 |
|
7 | A memoize decorator for Typescript.
|
8 |
|
9 | ## Installation
|
10 |
|
11 | ```
|
12 | npm install --save typescript-memoize
|
13 | ```
|
14 |
|
15 | ## Usage:
|
16 |
|
17 | ```typescript
|
18 | @Memoize(hashFunction?: (...args: any[]) => any)
|
19 | ```
|
20 |
|
21 | You can use it in four ways:
|
22 |
|
23 | * Memoize a `get` accessor,
|
24 | * Memoize a method which takes no parameters,
|
25 | * Memoize a method which varies based on its first parameter only,
|
26 | * Memoize a method which varies based on some combination of parameters
|
27 |
|
28 | You can call memoized methods *within* the same class, too. This could be useful if you want to memoize the return value for an entire data set, and also a filtered or mapped version of that same set.
|
29 |
|
30 | ## Memoize a `get` accessor, or a method which takes no parameters
|
31 |
|
32 | These both work the same way. Subsequent calls to a memoized method without parameters, or to a `get` accessor, always return the same value.
|
33 |
|
34 | I generally consider it an anti-pattern for a call to a `get` accessor to trigger an expensive operation. Simply adding `Memoize()` to a `get` allows for seamless lazy-loading.
|
35 |
|
36 | ```typescript
|
37 | import {Memoize,MemoizeExpiring} from 'typescript-memoize';
|
38 |
|
39 | class SimpleFoo {
|
40 |
|
41 | // Memoize a method without parameters
|
42 | @Memoize()
|
43 | public getAllTheData() {
|
44 | // do some expensive operation to get data
|
45 | return data;
|
46 | }
|
47 |
|
48 | // Memoize a method and expire the value after some time in milliseconds
|
49 | @MemoizeExpiring(5000)
|
50 | public getDataForSomeTime() {
|
51 | // do some expensive operation to get data
|
52 | return data;
|
53 | }
|
54 |
|
55 | // Memoize a getter
|
56 | @Memoize()
|
57 | public get someValue() {
|
58 | // do some expensive operation to calculate value
|
59 | return value;
|
60 | }
|
61 |
|
62 | }
|
63 | ```
|
64 |
|
65 | And then we call them from somewhere else in our code:
|
66 |
|
67 | ```typescript
|
68 | let simpleFoo = new SimpleFoo();
|
69 |
|
70 | // Memoizes a calculated value and returns it:
|
71 | let methodVal1 = simpleFoo.getAllTheData();
|
72 |
|
73 | // Returns memoized value
|
74 | let methodVal2 = simpleFoo.getAllTheData();
|
75 |
|
76 | // Memoizes (lazy-loads) a calculated value and returns it:
|
77 | let getterVal1 = simpleFoo.someValue;
|
78 |
|
79 | // Returns memoized value
|
80 | let getterVal2 = simpleFoo.someValue;
|
81 |
|
82 | ```
|
83 |
|
84 | ## Memoize a method which varies based on its first parameter only
|
85 |
|
86 | Subsequent calls to this style of memoized method will always return the same value.
|
87 |
|
88 | I'm not really sure why anyone would use this approach to memoize a method with *more* than one parameter, but it's possible.
|
89 |
|
90 | ```typescript
|
91 | import {Memoize} from 'typescript-memoize';
|
92 |
|
93 | class ComplicatedFoo {
|
94 |
|
95 | // Memoize a method without parameters (just like the first example)
|
96 | @Memoize()
|
97 | public getAllTheData() {
|
98 | // do some expensive operation to get data
|
99 | return data;
|
100 | }
|
101 |
|
102 | // Memoize a method with one parameter
|
103 | @Memoize()
|
104 | public getSomeOfTheData(id: number) {
|
105 | let allTheData = this.getAllTheData(); // if you want to!
|
106 | // do some expensive operation to get data
|
107 | return data;
|
108 | }
|
109 |
|
110 | // Memoize a method with multiple parameters
|
111 | // Only the first parameter will be used for memoization
|
112 | @Memoize()
|
113 | public getGreeting(name: string, planet: string) {
|
114 | return 'Hello, ' + name + '! Welcome to ' + planet;
|
115 | }
|
116 |
|
117 | }
|
118 | ```
|
119 |
|
120 | We call these methods from somewhere else in our code:
|
121 |
|
122 | ```typescript
|
123 | let complicatedFoo = new ComplicatedFoo();
|
124 |
|
125 | // Returns calculated value and memoizes it:
|
126 | let oneParam1 = complicatedFoo.getSomeOfTheData();
|
127 |
|
128 | // Returns memoized value
|
129 | let oneParam2 = complicatedFoo.getSomeOfTheData();
|
130 |
|
131 | // Memoizes a calculated value and returns it:
|
132 | // 'Hello, Darryl! Welcome to Earth'
|
133 | let greeterVal1 = complicatedFoo.getGreeting('Darryl', 'Earth');
|
134 |
|
135 | // Ignores the second parameter, and returns memoized value
|
136 | // 'Hello, Darryl! Welcome to Earth'
|
137 | let greeterVal2 = complicatedFoo.getGreeting('Darryl', 'Mars');
|
138 | ```
|
139 |
|
140 | ## Memoize a method which varies based on some combination of parameters
|
141 |
|
142 | Pass in a `hashFunction` which takes the same parameters as your target method, to memoize values based on all parameters, or some other custom logic
|
143 |
|
144 | ```typescript
|
145 | import {Memoize} from 'typescript-memoize';
|
146 |
|
147 | class MoreComplicatedFoo {
|
148 |
|
149 | // Memoize a method with multiple parameters
|
150 | // Memoize will remember values based on keys like: 'name;planet'
|
151 | @Memoize((name: string, planet: string) => {
|
152 | return name + ';' + planet;
|
153 | })
|
154 | public getBetterGreeting(name: string, planet: string) {
|
155 | return 'Hello, ' + name + '! Welcome to ' + planet;
|
156 | }
|
157 |
|
158 | // Memoize based on some other logic
|
159 | @Memoize(() => {
|
160 | return new Date();
|
161 | })
|
162 | public memoryLeak(greeting: string) {
|
163 | return greeting + '!!!!!';
|
164 | }
|
165 |
|
166 | // Memoize also accepts parameters via a single object argument
|
167 | @Memoize({
|
168 | expiring: 10000, // milliseconds
|
169 | hashFunction: (name: string, planet: string) => {
|
170 | return name + ';' + planet;
|
171 | }
|
172 | })
|
173 | public getSameBetterGreeting(name: string, planet: string) {
|
174 | return 'Hello, ' + name + '! Welcome to ' + planet;
|
175 | }
|
176 |
|
177 | }
|
178 | ```
|
179 |
|
180 | We call these methods from somewhere else in our code. By now you should be getting the idea:
|
181 |
|
182 | ```typescript
|
183 | let moreComplicatedFoo = new MoreComplicatedFoo();
|
184 |
|
185 | // 'Hello, Darryl! Welcome to Earth'
|
186 | let greeterVal1 = moreComplicatedFoo.getBetterGreeting('Darryl', 'Earth');
|
187 |
|
188 | // 'Hello, Darryl! Welcome to Mars'
|
189 | let greeterVal2 = moreComplicatedFoo.getBetterGreeting('Darryl', 'Mars');
|
190 |
|
191 | // Fill up the computer with useless greetings:
|
192 | let greeting = moreComplicatedFoo.memoryLeak('Hello');
|
193 |
|
194 | ```
|
195 |
|
196 | ## Memoize accepts one or more "tag" strings that allow the cache to be invalidated on command
|
197 |
|
198 | Passing an array with one or more "tag" strings these will allow you to later clear the cache of results associated with methods or the `get`accessors using the `clear()` function.
|
199 |
|
200 | The `clear()` function also requires an array of "tag" strings.
|
201 |
|
202 | ```typescript
|
203 | import {Memoize} from 'typescript-memoize';
|
204 |
|
205 | class ClearableFoo {
|
206 |
|
207 | // Memoize accepts tags
|
208 | @Memoize({ tags: ["foo", "bar"] })
|
209 | public getClearableGreeting(name: string, planet: string) {
|
210 | return 'Hello, ' + name + '! Welcome to ' + planet;
|
211 | }
|
212 |
|
213 |
|
214 | // Memoize accepts tags
|
215 | @Memoize({ tags: ["bar"] })
|
216 | public getClearableSum(a: number, b: number) {
|
217 | return a + b;
|
218 | }
|
219 |
|
220 | }
|
221 | ```
|
222 |
|
223 | We call these methods from somewhere else in our code.
|
224 |
|
225 | ```typescript
|
226 | import {clear} from 'typescript-memoize';
|
227 |
|
228 | let clearableFoo = new ClearableFoo();
|
229 |
|
230 | // 'Hello, Darryl! Welcome to Earth'
|
231 | let greeterVal1 = clearableFoo.getClearableGreeting('Darryl', 'Earth');
|
232 |
|
233 | // Ignores the second parameter, and returns memoized value
|
234 | // 'Hello, Darryl! Welcome to Earth'
|
235 | let greeterVal2 = clearableFoo.getClearableGreeting('Darryl', 'Mars');
|
236 |
|
237 | // '3'
|
238 | let sum1 = clearableFoo.getClearableSum(2, 1);
|
239 |
|
240 | // Ignores the second parameter, and returns memoized value
|
241 | // '3'
|
242 | let sum2 = clearableFoo.getClearableSum(2, 2);
|
243 |
|
244 | clear(["foo"]);
|
245 |
|
246 | // The memoized values are cleared, return a new value
|
247 | // 'Hello, Darryl! Welcome to Mars'
|
248 | let greeterVal3 = clearableFoo.getClearableGreeting('Darryl', 'Mars');
|
249 |
|
250 |
|
251 | // The memoized value is not associated with 'foo' tag, returns memoized value
|
252 | // '3'
|
253 | let sum3 = clearableFoo.getClearableSum(2, 2);
|
254 |
|
255 | clear(["bar"]);
|
256 |
|
257 | // The memoized values are cleared, return a new value
|
258 | // 'Hello, Darryl! Welcome to Earth'
|
259 | let greeterVal4 = clearableFoo.getClearableGreeting('Darryl', 'Earth');
|
260 |
|
261 |
|
262 | // The memoized values are cleared, return a new value
|
263 | // '4'
|
264 | let sum4 = clearableFoo.getClearableSum(2, 2);
|
265 |
|
266 | ```
|