typed-path
Advanced tools
Comparing version 2.0.2 to 2.1.0
export declare type TypedPathKey = string | symbol | number; | ||
export declare type TypedPathNode<T> = { | ||
$path: string; | ||
$raw: TypedPathKey[]; | ||
export declare type TypedPathFunction<T> = (...args: any[]) => T; | ||
export declare type TypedPathHandlersConfig = Record<string, <T extends TypedPathHandlersConfig = Record<never, never>>(path: string[], additionalHandlers?: T) => any>; | ||
declare const defaultHandlersConfig: { | ||
$path: (path: string[]) => string; | ||
$raw: (path: string[]) => string[]; | ||
toString: (path: string[]) => () => string; | ||
[Symbol.toStringTag]: (path: string[]) => () => string; | ||
valueOf: (path: string[]) => () => string; | ||
}; | ||
export declare type TypedPathFunction<T> = (...args: any[]) => T; | ||
export declare type TypedPathWrapper<T> = (T extends Array<infer Z> ? { | ||
[index: number]: TypedPathWrapper<Z>; | ||
export declare type TypedPathHandlers<T extends TypedPathHandlersConfig> = { | ||
[key in keyof T]: ReturnType<T[key]>; | ||
}; | ||
export declare type TypedPathWrapper<T, TPH extends TypedPathHandlers<Record<never, never>>> = (T extends Array<infer Z> ? { | ||
[index: number]: TypedPathWrapper<Z, TPH>; | ||
} : T extends TypedPathFunction<infer RET> ? { | ||
(): TypedPathWrapper<RET>; | ||
(): TypedPathWrapper<RET, TPH>; | ||
} & { | ||
[P in keyof RET]: TypedPathWrapper<RET[P]>; | ||
[P in keyof RET]: TypedPathWrapper<RET[P], TPH>; | ||
} : { | ||
[P in keyof T]: TypedPathWrapper<T[P]>; | ||
}) & TypedPathNode<T>; | ||
export declare function typedPath<T>(path?: string[]): TypedPathWrapper<T>; | ||
[P in keyof T]: TypedPathWrapper<T[P], TPH>; | ||
}) & TypedPathHandlers<TPH>; | ||
export declare function typedPath<T, K extends TypedPathHandlersConfig = Record<never, never>>(additionalHandlers?: K, path?: string[], defaultsApplied?: boolean): TypedPathWrapper<T, K & typeof defaultHandlersConfig>; | ||
export {}; |
44
index.js
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __spreadArrays = (this && this.__spreadArrays) || function () { | ||
@@ -9,8 +20,5 @@ for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; | ||
}; | ||
var _a; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var toStringMethods = [ | ||
'toString', | ||
Symbol.toStringTag, | ||
'valueOf' | ||
]; | ||
exports.typedPath = void 0; | ||
function pathToString(path) { | ||
@@ -27,16 +35,26 @@ return path.reduce(function (current, next) { | ||
} | ||
function typedPath(path) { | ||
var defaultHandlersConfig = (_a = { | ||
$path: function (path) { return pathToString(path); }, | ||
$raw: function (path) { return path; }, | ||
toString: function (path) { return function () { return pathToString(path); }; } | ||
}, | ||
_a[Symbol.toStringTag] = function (path) { return function () { return pathToString(path); }; }, | ||
_a.valueOf = function (path) { return function () { return pathToString(path); }; }, | ||
_a); | ||
function typedPath(additionalHandlers, path, defaultsApplied) { | ||
if (path === void 0) { path = []; } | ||
if (defaultsApplied === void 0) { defaultsApplied = false; } | ||
return new Proxy({}, { | ||
get: function (target, name) { | ||
if (name === '$path') { | ||
return pathToString(path); | ||
var handlersConfig; | ||
if (defaultsApplied) { | ||
handlersConfig = additionalHandlers; | ||
} | ||
if (name === '$raw') { | ||
return path; | ||
else { | ||
handlersConfig = __assign(__assign({}, (additionalHandlers !== null && additionalHandlers !== void 0 ? additionalHandlers : {})), defaultHandlersConfig); | ||
} | ||
if (toStringMethods.includes(name)) { | ||
return function () { return pathToString(path); }; | ||
if (handlersConfig.hasOwnProperty(name)) { | ||
return handlersConfig[name](path, additionalHandlers); | ||
} | ||
return typedPath(__spreadArrays(path, [name.toString()])); | ||
return typedPath(handlersConfig, __spreadArrays(path, [name.toString()]), true); | ||
} | ||
@@ -43,0 +61,0 @@ }); |
81
index.ts
@@ -0,61 +1,64 @@ | ||
function pathToString(path: string[]): string { | ||
return path.reduce((current, next) => { | ||
if (+next === +next) { | ||
current += `[${next}]`; | ||
} else { | ||
current += current === '' ? `${next}` : `.${next}`; | ||
} | ||
return current; | ||
}, ''); | ||
} | ||
export type TypedPathKey = string | symbol | number; | ||
export type TypedPathNode<T> = { | ||
$path: string; | ||
$raw: TypedPathKey[]; | ||
export type TypedPathFunction<T> = (...args: any[]) => T; | ||
export type TypedPathHandlersConfig = Record<string, <T extends TypedPathHandlersConfig = Record<never, never>>(path: string[], additionalHandlers?: T) => any>; | ||
const defaultHandlersConfig = { | ||
$path: (path: string[]) => pathToString(path), | ||
$raw: (path: string[]) => path, | ||
toString: (path: string[]) => () => pathToString(path), | ||
[Symbol.toStringTag]: (path: string[]) => () => pathToString(path), | ||
valueOf: (path: string[]) => () => pathToString(path), | ||
} | ||
export type TypedPathHandlers<T extends TypedPathHandlersConfig> = { | ||
[key in keyof T]: ReturnType<T[key]>; | ||
}; | ||
export type TypedPathFunction<T> = (...args: any[]) => T; | ||
export type TypedPathWrapper<T> = (T extends Array<infer Z> | ||
export type TypedPathWrapper<T, TPH extends TypedPathHandlers<Record<never, never>>> = (T extends Array<infer Z> | ||
? { | ||
[index: number]: TypedPathWrapper<Z>; | ||
[index: number]: TypedPathWrapper<Z, TPH>; | ||
} | ||
: T extends TypedPathFunction<infer RET> | ||
? { | ||
(): TypedPathWrapper<RET>; | ||
(): TypedPathWrapper<RET, TPH>; | ||
} & { | ||
[P in keyof RET]: TypedPathWrapper<RET[P]>; | ||
[P in keyof RET]: TypedPathWrapper<RET[P], TPH>; | ||
} | ||
: { | ||
[P in keyof T]: TypedPathWrapper<T[P]>; | ||
[P in keyof T]: TypedPathWrapper<T[P], TPH>; | ||
} | ||
) & TypedPathNode<T>; | ||
) & TypedPathHandlers<TPH>; | ||
const toStringMethods: (string | symbol | number)[] = [ | ||
'toString', | ||
Symbol.toStringTag, | ||
'valueOf' | ||
]; | ||
function pathToString(path: string[]): string { | ||
return path.reduce((current, next) => { | ||
if (+next === +next) { | ||
current += `[${next}]`; | ||
} else { | ||
current += current === '' ? `${next}` : `.${next}`; | ||
} | ||
return current; | ||
}, ''); | ||
} | ||
export function typedPath<T>(path: string[] = []): TypedPathWrapper<T> { | ||
return <TypedPathWrapper<T>>new Proxy({}, { | ||
export function typedPath<T, K extends TypedPathHandlersConfig = Record<never, never>>(additionalHandlers?: K, path: string[] = [], defaultsApplied: boolean = false): TypedPathWrapper<T, K & typeof defaultHandlersConfig> { | ||
return <TypedPathWrapper<T, K & typeof defaultHandlersConfig>>new Proxy({}, { | ||
get(target: T, name: TypedPathKey) { | ||
if (name === '$path') { | ||
return pathToString(path); | ||
} | ||
let handlersConfig: TypedPathHandlersConfig; | ||
if (name === '$raw') { | ||
return path; | ||
if (defaultsApplied) { | ||
handlersConfig = additionalHandlers; | ||
} else { | ||
handlersConfig = { ...(additionalHandlers ?? {}), ...defaultHandlersConfig }; | ||
} | ||
if (toStringMethods.includes(name)) { | ||
return () => pathToString(path); | ||
if (handlersConfig.hasOwnProperty(name)) { | ||
return handlersConfig[name as any](path, additionalHandlers); | ||
} | ||
return typedPath([...path, name.toString()]); | ||
return typedPath(handlersConfig, [...path, name.toString()], true); | ||
} | ||
}); | ||
} |
{ | ||
"name": "typed-path", | ||
"version": "2.0.2", | ||
"version": "2.1.0", | ||
"description": "Type safe object string paths for typescript.", | ||
@@ -42,6 +42,6 @@ "main": "index.js", | ||
"mocha": "^6.2.0", | ||
"ts-node": "8.3.0", | ||
"ts-node": "^9.0.0", | ||
"tslint": "^4.4.2", | ||
"typescript": "3.6.4" | ||
"typescript": "^4.0.5" | ||
} | ||
} |
@@ -23,3 +23,5 @@ # Typed Path | ||
Also you can get access to the path string using `$path` special field. [@m-abboud](https://github.com/m-abboud) | ||
#### .$path | ||
[@m-abboud](https://github.com/m-abboud) | ||
Also you can get access to the path string using `$path` special field. | ||
@@ -31,3 +33,5 @@ Like this: | ||
If you need a raw path, which is of type `string[]` - you can get it using `$raw` special field. [dcbrwn](https://github.com/dcbrwn) | ||
#### .$raw | ||
[@dcbrwn](https://github.com/dcbrwn) | ||
If you need a raw path, which is of type `string[]` - you can get it using `$raw` special field. | ||
```js | ||
@@ -37,3 +41,28 @@ console.log(tp<TestType>().a.b.c.d.$raw); // this will output ["a", "b", "c", "d"] | ||
#### Additional handlers | ||
[@nick-lvov-dev](https://github.com/nick-lvov-dev) | ||
You can extend path handlers functionality using additional handlers: | ||
```js | ||
const testAdditionalHandlers = { | ||
$url: (path: string[]) => path.join('/') | ||
} | ||
console.log(tp<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers).a.b.c.$url); // this will output "a/b/c" | ||
``` | ||
The additional handlers are also chainable: | ||
```js | ||
const testAdditionalHandlers = { | ||
$abs: (path: string[]) => typedPath<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers, ['', ...path]), | ||
$url: (path: string[]) => path.join('/') | ||
} | ||
console.log(tp<TestType, typeof testAdditionalHandlers>(testAdditionalHandlers).a.b.c.$abs.$url); // this will output "/a/b/c" | ||
``` | ||
--- | ||
### Suggestions | ||
@@ -40,0 +69,0 @@ |
Sorry, the diff of this file is not supported yet
11399
138
75