Skip to content

Commit 73914a4

Browse files
committed
Clean up types
1 parent c5104dd commit 73914a4

File tree

5 files changed

+58
-37
lines changed

5 files changed

+58
-37
lines changed

addon/index.ts

+30-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { TrackedMap } from 'tracked-maps-and-sets';
22

3+
type PropertyDescriptorInit = PropertyDescriptor & { initializer: () => void };
4+
35
const managedKeys = new Set();
46
const localStorageCache = new TrackedMap();
57

@@ -25,16 +27,29 @@ window.addEventListener('storage', function ({ key, newValue }) {
2527
localStorageCache.set(key, jsonParseAndFreeze(newValue as string));
2628
});
2729

28-
export default function localStorageDecoratorFactory(...args: unknown[]) {
30+
export default function localStorageDecoratorFactory(
31+
target: unknown,
32+
propertyKey: PropertyKey
33+
): void;
34+
35+
export default function localStorageDecoratorFactory(
36+
customLocalStorageKey: string
37+
): (
38+
target: unknown,
39+
propertyKey: PropertyKey,
40+
descriptor: PropertyDescriptorInit
41+
) => void;
42+
43+
export default function localStorageDecoratorFactory(
44+
...args: [unknown, PropertyKey, PropertyDescriptorInit]
45+
) {
2946
const isDirectDecoratorInvocation = isElementDescriptor(...args);
3047
const customLocalStorageKey = isDirectDecoratorInvocation
3148
? undefined
3249
: args[0];
3350

3451
function localStorageDecorator(
35-
target: unknown,
36-
key: PropertyKey,
37-
descriptor: PropertyDecorator & { initializer: () => void }
52+
...[target, key, descriptor]: [unknown, PropertyKey, PropertyDescriptorInit]
3853
) {
3954
const localStorageKey = customLocalStorageKey ?? key;
4055

@@ -45,7 +60,7 @@ export default function localStorageDecoratorFactory(...args: unknown[]) {
4560
get() {
4661
return (
4762
localStorageCache.get(localStorageKey) ??
48-
(descriptor.initializer
63+
(descriptor?.initializer
4964
? descriptor.initializer.call(target)
5065
: undefined)
5166
);
@@ -64,8 +79,7 @@ export default function localStorageDecoratorFactory(...args: unknown[]) {
6479
}
6580

6681
return isDirectDecoratorInvocation
67-
? // @ts-expect-error A spread argument must either have a tuple type or be passed to a rest parameter.
68-
localStorageDecorator(...args)
82+
? localStorageDecorator(...args)
6983
: localStorageDecorator;
7084
}
7185

@@ -82,18 +96,22 @@ export function initalizeLocalStorageKey(key: string) {
8296
// before it is set.
8397
if (!managedKeys.has(key)) {
8498
managedKeys.add(key);
85-
localStorageCache.set(
86-
key,
87-
jsonParseAndFreeze(window.localStorage.getItem(key) as string)
88-
);
99+
100+
const item = window.localStorage.getItem(key);
101+
if (item === null) {
102+
throw new Error();
103+
}
104+
localStorageCache.set(key, jsonParseAndFreeze(item));
89105
}
90106
}
91107

92108
// This will detect if the function arguments match the legacy decorator pattern
93109
//
94110
// Borrowed from the Ember Data source code:
95111
// https://github.com/emberjs/data/blob/22a8f20e2f11ed82c85160944e976073dc530d8b/packages/model/addon/-private/util.ts#L5
96-
function isElementDescriptor(...args: unknown[]) {
112+
function isElementDescriptor(
113+
...args: [unknown, PropertyKey, PropertyDescriptor]
114+
): boolean {
97115
const [maybeTarget, maybeKey, maybeDescriptor] = args;
98116

99117
return (

tests/dummy/app/components/test-component.js tests/dummy/app/components/test-component.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import localStorage from 'ember-local-storage-decorator';
44

55
export default class TestComponentComponent extends Component {
66
@localStorage
7-
foo;
7+
foo: unknown;
88

99
@localStorage
10-
bar;
10+
bar: unknown;
1111

1212
@action
1313
updateFoo() {
File renamed without changes.

tests/integration/components/test-component-test.js tests/integration/components/test-component-test.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,24 @@ module('Integration | Component | test-component', function (hooks) {
1515
test('it works inside a component', async function (assert) {
1616
await render(hbs`<TestComponent />`);
1717
assert.dom().hasText('');
18-
assert.equal(JSON.parse(window.localStorage.getItem('foo')), null);
18+
assert.equal(JSON.parse(window.localStorage.getItem('foo')!), null);
1919

2020
await click('button');
2121
assert.dom().hasText('foo');
22-
assert.equal(JSON.parse(window.localStorage.getItem('foo')), 'foo');
22+
assert.equal(JSON.parse(window.localStorage.getItem('foo')!), 'foo');
2323
});
2424

2525
test('setting one property does not invalidate another', async function (assert) {
26-
let invalidationCounter = {
26+
const invalidationCounter = {
2727
foo: 0,
2828
bar: 0,
2929
};
30-
this.set('invalidationTracker', (property) => {
31-
invalidationCounter[property]++;
32-
});
30+
this.set(
31+
'invalidationTracker',
32+
(property: keyof typeof invalidationCounter) => {
33+
invalidationCounter[property]++;
34+
}
35+
);
3336
await render(
3437
hbs`<TestComponent @onInvalidation={{this.invalidationTracker}} />`
3538
);

tests/unit/decorators/local-storage-test.js tests/unit/decorators/local-storage-test.ts

+17-17
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module('Unit | Decorator | @localStorage', function (hooks) {
1616
module('default values', function () {
1717
test('it defaults to null', function (assert) {
1818
const klass = new (class {
19-
@localStorage() foo;
19+
@localStorage() foo: unknown;
2020
})();
2121

2222
assert.equal(klass.foo, null);
@@ -46,7 +46,7 @@ module('Unit | Decorator | @localStorage', function (hooks) {
4646
window.localStorage.setItem('foo', JSON.stringify('baz'));
4747

4848
const klass = new (class {
49-
@localStorage() foo;
49+
@localStorage() foo: string | undefined;
5050
})();
5151

5252
assert.equal(klass.foo, 'baz');
@@ -56,19 +56,19 @@ module('Unit | Decorator | @localStorage', function (hooks) {
5656
window.localStorage.setItem('foo', JSON.stringify([{ bar: 'baz' }]));
5757

5858
const klass = new (class {
59-
@localStorage() foo;
59+
@localStorage() foo: unknown[] | undefined;
6060
})();
6161

6262
assert.deepEqual(klass.foo, [{ bar: 'baz' }]);
6363
assert.ok(Object.isFrozen(klass.foo), 'freezes complex value');
64-
assert.ok(Object.isFrozen(klass.foo[0]), 'deep freezes complex value');
64+
assert.ok(Object.isFrozen(klass.foo![0]), 'deep freezes complex value');
6565
});
6666

6767
test('supports custom local storage key', function (assert) {
6868
window.localStorage.setItem('bar', JSON.stringify('baz'));
6969

7070
const klass = new (class {
71-
@localStorage('bar') foo;
71+
@localStorage('bar') foo: string | undefined;
7272
})();
7373

7474
assert.equal(klass.foo, 'baz');
@@ -78,7 +78,7 @@ module('Unit | Decorator | @localStorage', function (hooks) {
7878
module('setter', function () {
7979
test('user can set a simple value', function (assert) {
8080
const klass = new (class {
81-
@localStorage() foo;
81+
@localStorage() foo: string | undefined;
8282
})();
8383

8484
klass.foo = 'bar';
@@ -87,7 +87,7 @@ module('Unit | Decorator | @localStorage', function (hooks) {
8787

8888
test('user can set a complex value', function (assert) {
8989
const klass = new (class {
90-
@localStorage() foo;
90+
@localStorage() foo: unknown[] | undefined;
9191
})();
9292

9393
klass.foo = [{ bar: 'baz' }];
@@ -96,22 +96,22 @@ module('Unit | Decorator | @localStorage', function (hooks) {
9696

9797
test('persistes simple value in local storage', function (assert) {
9898
const klass = new (class {
99-
@localStorage() foo;
99+
@localStorage() foo: string | undefined;
100100
})();
101101

102102
klass.foo = 'baz';
103-
assert.equal(JSON.parse(window.localStorage.getItem('foo')), 'baz');
103+
assert.equal(JSON.parse(window.localStorage.getItem('foo')!), 'baz');
104104
});
105105

106106
test('persists complex value in local storage', function (assert) {
107107
const klass = new (class {
108-
@localStorage() foo;
108+
@localStorage() foo: unknown[] | undefined;
109109
})();
110110

111111
const value = [{ bar: 'baz' }];
112112

113113
klass.foo = value;
114-
assert.deepEqual(JSON.parse(window.localStorage.getItem('foo')), value);
114+
assert.deepEqual(JSON.parse(window.localStorage.getItem('foo')!), value);
115115
assert.ok(Object.isFrozen(klass.foo), 'object is frozen');
116116
assert.ok(Object.isFrozen(klass.foo[0]), 'object is deep frozen');
117117
assert.notOk(klass.foo === value, 'object is a copy of the one set');
@@ -120,21 +120,21 @@ module('Unit | Decorator | @localStorage', function (hooks) {
120120

121121
test('supports custom local storage key', function (assert) {
122122
const klass = new (class {
123-
@localStorage('bar') foo;
123+
@localStorage('bar') foo: unknown;
124124
})();
125125

126126
klass.foo = 'baz';
127-
assert.equal(JSON.parse(window.localStorage.getItem('bar')), 'baz');
127+
assert.equal(JSON.parse(window.localStorage.getItem('bar')!), 'baz');
128128
});
129129
});
130130

131131
module('external changes', function () {
132132
test('picks up changes caused by another class', function (assert) {
133133
const klassA = new (class {
134-
@localStorage() foo;
134+
@localStorage() foo: unknown;
135135
})();
136136
const klassB = new (class {
137-
@localStorage() foo;
137+
@localStorage() foo: unknown;
138138
})();
139139

140140
klassA.foo = 'bar';
@@ -143,7 +143,7 @@ module('Unit | Decorator | @localStorage', function (hooks) {
143143

144144
test('picks up changes caused by other tabs', async function (assert) {
145145
const klass = new (class {
146-
@localStorage() foo;
146+
@localStorage() foo: unknown;
147147
})();
148148

149149
// assert initial state
@@ -165,7 +165,7 @@ module('Unit | Decorator | @localStorage', function (hooks) {
165165
test('developer can reinitialize a local storage key in tests', function (assert) {
166166
// create a class, which will be used between multiple tests runs
167167
class Foo {
168-
@localStorage foo;
168+
@localStorage foo: unknown;
169169
}
170170

171171
// use the class in first test

0 commit comments

Comments
 (0)