Skip to content

Commit 210620f

Browse files
authored
Merge pull request #8 from nick-lvov-dev/additional-handlers-config
Additional handlers config
2 parents cdc4053 + 9a5a5fb commit 210620f

File tree

5 files changed

+104
-59
lines changed

5 files changed

+104
-59
lines changed

README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,47 @@ With `typed-path` typescript can check paths and warns you about errors.
2020

2121
![](http://res.cloudinary.com/daren64mz/image/upload/v1487457505/tp-refactoring_p4byr3.gif)
2222

23-
Also you can get access to the path string using `$path` special field. [@m-abboud](https://github.com/m-abboud)
23+
#### .$path
24+
[@m-abboud](https://github.com/m-abboud)
25+
Also you can get access to the path string using `$path` special field.
2426

2527
Like this:
2628
```js
2729
console.log(tp<TestType>().a.b.c.d.$path); // this will output "a.b.c.d"
2830
```
2931

30-
If you need a raw path, which is of type `string[]` - you can get it using `$raw` special field. [dcbrwn](https://github.com/dcbrwn)
32+
#### .$raw
33+
[@dcbrwn](https://github.com/dcbrwn)
34+
If you need a raw path, which is of type `string[]` - you can get it using `$raw` special field.
3135
```js
3236
console.log(tp<TestType>().a.b.c.d.$raw); // this will output ["a", "b", "c", "d"]
3337
```
3438

39+
#### Additional handlers
40+
[@nick-lvov-dev](https://github.com/nick-lvov-dev)
41+
42+
You can extend path handlers functionality using additional handlers:
43+
44+
```js
45+
const testAdditionalHandlers = {
46+
$url: (path: string[]) => path.join('/')
47+
}
48+
49+
console.log(tp<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers).a.b.c.$url); // this will output "a/b/c"
50+
```
51+
52+
The additional handlers are also chainable:
53+
54+
```js
55+
const testAdditionalHandlers = {
56+
$abs: (path: string[]) => typedPath<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers, ['', ...path]),
57+
$url: (path: string[]) => path.join('/')
58+
}
59+
60+
console.log(tp<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers).a.b.c.$abs.$url); // this will output "/a/b/c"
61+
```
62+
63+
---
3564

3665
### Suggestions
3766

index.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ type TestType = {
1717
}
1818
};
1919

20+
const testAdditionalHandlers = {
21+
$abs: (path: string[]) => typedPath<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers, ['', ...path]),
22+
$url: (path: string[]) => path.join('/')
23+
}
24+
2025
describe('Typed path', () => {
2126
it('should get field path', () => {
2227
expect(typedPath<TestType>().a.b.c.$path).to.equal('a.b.c');
@@ -60,4 +65,12 @@ describe('Typed path', () => {
6065
it('should get path for valueOf()', () => {
6166
expect(tp<TestType>().a.b.f[3].blah.path.valueOf()).to.equal('a.b.f[3].blah.path');
6267
});
68+
69+
it('should work with extended handlers', () => {
70+
expect(tp<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers).a.b.c.$url).to.equal('a/b/c');
71+
})
72+
73+
it('should work with chained extended handlers', () => {
74+
expect(tp<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers).a.b.c.$abs.$url).to.equal('/a/b/c');
75+
})
6376
});

index.ts

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,64 @@
1-
export type TypedPathKey = string | symbol | number;
1+
function pathToString(path: string[]): string {
2+
return path.reduce((current, next) => {
3+
if (+next === +next) {
4+
current += `[${next}]`;
5+
} else {
6+
current += current === '' ? `${next}` : `.${next}`;
7+
}
28

3-
export type TypedPathNode<T> = {
4-
$path: string;
5-
$raw: TypedPathKey[];
6-
};
9+
return current;
10+
}, '');
11+
}
12+
13+
export type TypedPathKey = string | symbol | number;
714

815
export type TypedPathFunction<T> = (...args: any[]) => T;
916

10-
export type TypedPathWrapper<T> = (T extends Array<infer Z>
17+
export type TypedPathHandlersConfig = Record<string, <T extends TypedPathHandlersConfig = Record<never, never>>(path: string[], additionalHandlers?: T) => any>;
18+
19+
const defaultHandlersConfig = {
20+
$path: (path: string[]) => pathToString(path),
21+
$raw: (path: string[]) => path,
22+
toString: (path: string[]) => () => pathToString(path),
23+
[Symbol.toStringTag]: (path: string[]) => () => pathToString(path),
24+
valueOf: (path: string[]) => () => pathToString(path),
25+
}
26+
27+
export type TypedPathHandlers<T extends TypedPathHandlersConfig> = {
28+
[key in keyof T]: ReturnType<T[key]>;
29+
};
30+
31+
export type TypedPathWrapper<T, TPH extends TypedPathHandlers<Record<never, never>>> = (T extends Array<infer Z>
1132
? {
12-
[index: number]: TypedPathWrapper<Z>;
33+
[index: number]: TypedPathWrapper<Z, TPH>;
1334
}
1435
: T extends TypedPathFunction<infer RET>
1536
? {
16-
(): TypedPathWrapper<RET>;
37+
(): TypedPathWrapper<RET, TPH>;
1738
} & {
18-
[P in keyof RET]: TypedPathWrapper<RET[P]>;
39+
[P in keyof RET]: TypedPathWrapper<RET[P], TPH>;
1940
}
2041
: {
21-
[P in keyof T]: TypedPathWrapper<T[P]>;
22-
}
23-
) & TypedPathNode<T>;
24-
25-
const toStringMethods: (string | symbol | number)[] = [
26-
'toString',
27-
Symbol.toStringTag,
28-
'valueOf'
29-
];
30-
31-
function pathToString(path: string[]): string {
32-
return path.reduce((current, next) => {
33-
if (+next === +next) {
34-
current += `[${next}]`;
35-
} else {
36-
current += current === '' ? `${next}` : `.${next}`;
42+
[P in keyof T]: TypedPathWrapper<T[P], TPH>;
3743
}
44+
) & TypedPathHandlers<TPH>;
3845

39-
return current;
40-
}, '');
41-
}
42-
43-
export function typedPath<T>(path: string[] = []): TypedPathWrapper<T> {
44-
return <TypedPathWrapper<T>>new Proxy({}, {
46+
export function typedPath<T, K extends TypedPathHandlersConfig = Record<never, never>>(additionalHandlers?: K, path: string[] = [], defaultsApplied: boolean = false): TypedPathWrapper<T, K & typeof defaultHandlersConfig> {
47+
return <TypedPathWrapper<T, K & typeof defaultHandlersConfig>>new Proxy({}, {
4548
get(target: T, name: TypedPathKey) {
46-
if (name === '$path') {
47-
return pathToString(path);
48-
}
49+
let handlersConfig: TypedPathHandlersConfig;
4950

50-
if (name === '$raw') {
51-
return path;
51+
if (defaultsApplied) {
52+
handlersConfig = additionalHandlers;
53+
} else {
54+
handlersConfig = { ...(additionalHandlers ?? {}), ...defaultHandlersConfig };
5255
}
5356

54-
if (toStringMethods.includes(name)) {
55-
return () => pathToString(path);
57+
if (handlersConfig.hasOwnProperty(name)) {
58+
return handlersConfig[name as any](path, additionalHandlers);
5659
}
5760

58-
return typedPath([...path, name.toString()]);
61+
return typedPath(handlersConfig, [...path, name.toString()], true);
5962
}
6063
});
6164
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "typed-path",
3-
"version": "2.0.2",
3+
"version": "2.1.0",
44
"description": "Type safe object string paths for typescript.",
55
"main": "index.js",
66
"repository": {
@@ -39,8 +39,8 @@
3939
"chai": "^3.5.0",
4040
"husky": "^0.13.1",
4141
"mocha": "^6.2.0",
42-
"ts-node": "8.3.0",
42+
"ts-node": "^9.0.0",
4343
"tslint": "^4.4.2",
44-
"typescript": "3.6.4"
44+
"typescript": "^4.0.5"
4545
}
4646
}

yarn.lock

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -920,10 +920,10 @@ slide@^1.1.5:
920920
version "1.1.6"
921921
resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
922922

923-
source-map-support@^0.5.6:
924-
version "0.5.13"
925-
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
926-
integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
923+
source-map-support@^0.5.17:
924+
version "0.5.19"
925+
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
926+
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
927927
dependencies:
928928
buffer-from "^1.0.0"
929929
source-map "^0.6.0"
@@ -1034,16 +1034,16 @@ timed-out@^3.0.0:
10341034
version "3.1.3"
10351035
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217"
10361036

1037-
ts-node@8.3.0:
1038-
version "8.3.0"
1039-
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.3.0.tgz#e4059618411371924a1fb5f3b125915f324efb57"
1040-
integrity sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==
1037+
ts-node@^9.0.0:
1038+
version "9.0.0"
1039+
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3"
1040+
integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==
10411041
dependencies:
10421042
arg "^4.1.0"
10431043
diff "^4.0.1"
10441044
make-error "^1.1.1"
1045-
source-map-support "^0.5.6"
1046-
yn "^3.0.0"
1045+
source-map-support "^0.5.17"
1046+
yn "3.1.1"
10471047

10481048
tslint@^4.4.2:
10491049
version "4.4.2"
@@ -1066,10 +1066,10 @@ type-detect@^1.0.0:
10661066
version "1.0.0"
10671067
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
10681068

1069-
typescript@3.6.4:
1070-
version "3.6.4"
1071-
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d"
1072-
integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==
1069+
typescript@^4.0.5:
1070+
version "4.0.5"
1071+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389"
1072+
integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==
10731073

10741074
unzip-response@^1.0.2:
10751075
version "1.0.2"
@@ -1196,7 +1196,7 @@ [email protected], yargs@^13.3.0:
11961196
y18n "^4.0.0"
11971197
yargs-parser "^13.1.1"
11981198

1199-
yn@^3.0.0:
1199+
yn@3.1.1:
12001200
version "3.1.1"
12011201
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
12021202
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==

0 commit comments

Comments
 (0)