Skip to content

Commit 4ac2d51

Browse files
Add type safe get
1 parent 33a0637 commit 4ac2d51

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

addon/utils/type-safe-get.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { get } from '@ember/object';
2+
3+
function arrayGet(obj: any, props: string[]) {
4+
if (obj === undefined) return obj;
5+
return get(obj as any, props.join('.')) as any | undefined;
6+
}
7+
8+
function typeSafeGet<
9+
T extends any, A extends keyof T, B extends keyof T[A], C extends keyof T[A][B], D extends keyof T[A][B][C], E extends keyof T[A][B][C][D], F extends keyof T[A][B][C][D][E], G extends keyof T[A][B][C][D][E][F], H extends keyof T[A][B][C][D][E][F][G]
10+
>(
11+
object: T, prop1: A, prop2: B, prop3: C, prop4: D, prop5: E, prop6: F, prop7: G, prop8: H
12+
): T[A][B][C][D][E][F][G][H] | undefined
13+
14+
function typeSafeGet<
15+
T extends any, A extends keyof T, B extends keyof T[A], C extends keyof T[A][B], D extends keyof T[A][B][C], E extends keyof T[A][B][C][D], F extends keyof T[A][B][C][D][E], G extends keyof T[A][B][C][D][E][F]
16+
>(
17+
object: T, prop1: A, prop2: B, prop3: C, prop4: D, prop5: E, prop6: F, prop7: G
18+
): T[A][B][C][D][E][F][G] | undefined
19+
20+
function typeSafeGet<
21+
T extends any, A extends keyof T, B extends keyof T[A], C extends keyof T[A][B], D extends keyof T[A][B][C], E extends keyof T[A][B][C][D], F extends keyof T[A][B][C][D][E]
22+
>(
23+
object: T, prop1: A, prop2: B, prop3: C, prop4: D, prop5: E, prop6: F
24+
): T[A][B][C][D][E][F] | undefined
25+
26+
function typeSafeGet<
27+
T extends any, A extends keyof T, B extends keyof T[A], C extends keyof T[A][B], D extends keyof T[A][B][C], E extends keyof T[A][B][C][D]
28+
>(
29+
object: T, prop1: A, prop2: B, prop3: C, prop4: D, prop5: E
30+
): T[A][B][C][D][E] | undefined
31+
32+
function typeSafeGet<
33+
T extends any, A extends keyof T, B extends keyof T[A], C extends keyof T[A][B], D extends keyof T[A][B][C]
34+
>(
35+
object: T, prop1: A, prop2: B, prop3: C, prop4: D
36+
): T[A][B][C][D] | undefined
37+
38+
function typeSafeGet<
39+
T extends any, A extends keyof T, B extends keyof T[A], C extends keyof T[A][B]
40+
>(
41+
object: T, prop1: A, prop2: B, prop3: C
42+
): T[A][B][C] | undefined
43+
44+
function typeSafeGet<
45+
T extends any, A extends keyof T, B extends keyof T[A]
46+
>(
47+
object: T, prop1: A, prop2: B
48+
): T[A][B] | undefined
49+
50+
function typeSafeGet<
51+
T extends any, A extends keyof T
52+
>(
53+
object: T, prop: A
54+
): T[A] | undefined
55+
56+
function typeSafeGet<T extends any>(object: object, ...props: string[]): T | undefined {
57+
return arrayGet(object, props) as T | undefined;
58+
}
59+
60+
export function overrideGet<T extends any>(object: object, propPath: string): T | undefined
61+
export function overrideGet<T extends any>(object: object, ...props: string[]): T | undefined
62+
export function overrideGet<T extends any>(object: object, ...propPath: string[]): T | undefined {
63+
return arrayGet(object, propPath) as T | undefined;
64+
}
65+
66+
export default typeSafeGet;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import typeSafeGet, { overrideGet } from 'ember-typescript-utils/utils/type-safe-get';
2+
import { module, test } from 'qunit';
3+
4+
module('Unit | Utility | type-safe-get', function(_hooks) {
5+
6+
test('it works', function(assert) {
7+
const test = {a: {b: {c: {d: {e: {f: {g: {h: 'made it!'}}}}}}}};
8+
9+
assert.deepEqual(typeSafeGet(test, 'a'), test.a);
10+
assert.deepEqual(typeSafeGet(test, 'a', 'b'), test.a.b);
11+
assert.deepEqual(typeSafeGet(test, 'a', 'b', 'c'), test.a.b.c);
12+
assert.deepEqual(typeSafeGet(test, 'a', 'b', 'c', 'd'), test.a.b.c.d);
13+
assert.deepEqual(typeSafeGet(test, 'a', 'b', 'c', 'd', 'e'), test.a.b.c.d.e);
14+
assert.deepEqual(typeSafeGet(test, 'a', 'b', 'c', 'd', 'e', 'f'), test.a.b.c.d.e.f);
15+
assert.deepEqual(typeSafeGet(test, 'a', 'b', 'c', 'd', 'e', 'f', 'g'), test.a.b.c.d.e.f.g);
16+
assert.deepEqual(typeSafeGet(test, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), test.a.b.c.d.e.f.g.h);
17+
});
18+
19+
test('overrideGet also works', function(assert) {
20+
const test = {a: {b: {c: {d: {e: {f: {g: {h: 'made it!'}}}}}}}};
21+
22+
assert.deepEqual(overrideGet(test, 'a'), test.a);
23+
assert.deepEqual(overrideGet(test, 'a', 'b'), test.a.b);
24+
assert.deepEqual(overrideGet(test, 'a', 'b', 'c'), test.a.b.c);
25+
assert.deepEqual(overrideGet(test, 'a', 'b', 'c', 'd'), test.a.b.c.d);
26+
assert.deepEqual(overrideGet(test, 'a', 'b', 'c', 'd', 'e'), test.a.b.c.d.e);
27+
assert.deepEqual(overrideGet(test, 'a', 'b', 'c', 'd', 'e', 'f'), test.a.b.c.d.e.f);
28+
assert.deepEqual(overrideGet(test, 'a', 'b', 'c', 'd', 'e', 'f', 'g'), test.a.b.c.d.e.f.g);
29+
assert.deepEqual(overrideGet(test, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), test.a.b.c.d.e.f.g.h);
30+
});
31+
32+
test('it returns undefined if the initial object is undefined', function(assert) {
33+
let nothing!: {oops: 'this is just a type, not a value'};
34+
assert.equal(typeSafeGet(nothing, 'oops'), undefined);
35+
});
36+
});

0 commit comments

Comments
 (0)