A tool set for JavaScript programming.
npm install @jcstdio/jc-utilsshell
npm i @jcstdio/jc-utils
or
yarn add @jcstdio/jc-utils
or
pnpm i @jcstdio/jc-utils
`
judge tools (isXXX)
This part of the function is mainly used for all kinds of judgments, which are some of the most commonly used judgment tools. In fact, many open source projects have repeatedly implemented them, so it is necessary to encapsulate them.
`ts
// Such as:
import { isBoolean } from '@jcstdio/jc-utils'
`
$3
This part is mostly type-related judgments.
| Tool name | describe |
|:-|:-|
| isBoolean | Determine whether it is a Boolean value.
| isString | Determine whether it is a string.
| isNumber | Determine whether it is a number, excluding numbers represented by strings, etc.
| isNaNNumber | Determine whether it is NaN or not, so as to avoid the native isNaN function treating undefined equivalence as nan.
| isNotNaNNumber | Determine whether it is a non-NaN number.
| isInfinity | Determine whether it is infinite value Infinity.
| isNotInfinityNumber | A number that determines whether it is not Infinity.
| isNotNaNOrInfinityNumber | Determine whether it is a number that is not NaN and Infinity.
| isOddNumber | Determine whether a number is odd.
| isEvenNumber | Determine whether a number is an even number.
| isInt | Determine whether it is an integer.
| isObject | Determine whether it is an plain object.
| isSymbol | Determines whether it is a value of type symbol.
| isArray | Determine whether it is an array.
| isInt8Array | Determine whether it is an Int8Array instance.
| isUint8Array | Determine whether it is an instance of Uint8Array.
| isUint8ClampedArray | Determine whether it is an instance of Uint8ClampedArray.
| isInt16Array | Determine whether it is an instance of Int16Array.
| isUint16Array | Determine whether it is an instance of Uint16Array.
| isInt32Array | Determine whether it is an instance of Int32Array.
| isUint32Array | Determine whether it is an instance of Uint32Array.
| isBigInt64Array | Determine whether it is an instance of BigInt64Array.
| isBigUnit64Array | Determine whether it is an instance of BigUnit64Array.
| isFloat32Array | Determine whether it is an instance of Float32Array.
| isFloat64Array | Determine whether it is an instance of Float64Array.
| isArrayBuffer | Is it an ArrayBuffer instance?
| isDataView | Determine whether it is a DataView instance.
| isFunction | Determine whether it is a function.
| isMap | Determine whether it is a map (ES6 Map)
| isWeakMap | Determine whether it is a WeakMap instance.
| isSet | Determine whether it is a set (ES6 Set)
| isWeakSet | Determine whether it is a WeakSet instance.
| isWeakRef | Determine whether it is an instance of WeakRef.
| isRegExp | Determine whether it is regular or not.
| isDate | Determine whether it is a Date instance.
| isError | Determine whether it is an Error instance.
| isPrimitiver | Judge whether it is the original value.
| isPromise | Determine whether it is Promise.
| isDefined | Determine whether it is undefined.
| isUndefined | Determine whether it is undefined.
| isNull | Determine whether it is null.
| isNullAndUndefined | Returns true when isUndefined and isNull holds.
| isNullOrUndefined | Returns true when isNull or isUndefined.
$3
This set of isXXX tools mainly detects whether it is a string in a certain format through regular expressions. Personally, I think these tools may be used more, and you can also quickly encapsulate them through the ready-made regular expression variables in 12. Regularization and Regularization Tools (using the test method of regular objects).
| Tool name | describe |
|:-|:-|
isChinese | Whether the match is a Chinese character.
isEnglish | Indicates whether it is an English letter.
isUrl | Indicates whether it is a URL address.
isPhone | A string indicating whether it is a mobile phone number.
isEmail | Indicates whether it is a mailbox address.
isIdCard | Indicates whether it is an ID number string.
isNumberStr | Represents a numeric string.
isDecimalStr | Represents a decimal string
isIntegerStr | Represents an integer string
isTime | Represents the time string format: hh:mm:ss
isPostalCode | String representing postal code
isHtmlTag | Represents an HTML tag string
isQQ | String representing QQ number
isWechat | Represents a micro signal string
isLicensePlate | String representing license plate number
isHexColor | A string represen a hexadecimal color value
isChineseName | String representing Chinese name
isBankCard | A string represen that bank card number.
isNumberAndLetter | A string represen a combination of numbers and letter.
isChineseEnglishNumberUnderline | Represents a string consisting of Chinese, English, numbers and underscores.
isChineseEnglishNumberSpace | Represents a string consisting of Chinese, English, numbers and spaces.
isChineseEnglishNumberDash | Represents a string consisting of Chinese, English, numbers and dash lines.
isChineseEnglishNumberSlash | Represents a string consisting of Chinese, English, numbers and slashes.
isChineseEnglishNumberColon | Represents a string consisting of Chinese, English, numbers and colons.
isChineseEnglishNumberQuestion | Represents a string consisting of Chinese, English, numbers and question marks.
isChineseEnglishNumberExclamation | Represents a string consisting of Chinese, English, numbers and exclamation points.
#@# The third set of isXXX tools: runtime
| Tool name | describe |
|:-|:-|
isBrowser | Whether it is a browser.
isServer | Whether it is a server-side environment or not, compared with isNode, the tool will not check process.versions.node
isNode | Determine whether it is a NodeJS environment.
isElectron | Determine whether it is an election environment.
isBrowserAndNode | Meet the needs of both browsers and NodeJS operating environments, such as electron.
isNotBrowser | Not a browser environment
isNotServer | Not a server environment
event tools
$3
`js
import { EventEmitter } from '@jcstdio/jc-utils'
`
$3
`js
import { StateManager } from '@jcstdio/jc-utils'
const stateManager = new StateManager<{
value:number
}>({ value: 0, });
const sideEffec1 = (state: { value: any; }) => {
console.log('我是 sideEffec1 ,状态被更新,当前状态值为:', state.value);
}
const sideEffec2 = () => {
console.log('我是 sideEffec2 ,执行顺序为执行 “subscribe” 方法的顺序。')
}
// Set the callback after status update, which can be called by chain.
stateManager
.subscribe(sideEffec1)
.subscribe(sideEffec2)
// Update your status.
console.log('------- 第一次改变状态 -------');
stateManager.state = { value: 1 };
console.log('\n------- 第二次改变状态 -------');
stateManager
.unsubscribe(sideEffec2) // Cancel sideEffec2 subscribed when the status changes.
.state = { value: 2 }; // Execute subscriptions that have not been cancelled.
`
output is:
`
------- 第一次改变状态 -------
我是 sideEffec1 ,状态被更新,当前状态值为: 1
我是 sideEffec2 ,执行顺序为执行 “subscribe” 方法的顺序。
------- 第二次改变状态 -------
我是 sideEffec1 ,状态被更新,当前状态值为: 2
`
ID Manager
$3
In daily development, we often have the need to automatically assign a unique identifier (ID) to something. The ID manager is a unique identification value manager that stores uniformly and
uses dispersedly. With this manager, you can easily assign IDs to many different things-you only need to specify different things' names, so that one thing can be applied for non-repetitive identification strings.
$3
The so-called thing is only any category that needs to use ID to identify its different individuals. For example, a student in a class can be regarded as a thing, and the student's student number can be managed as its ID. The purpose of defining things is to distinguish these things. For example, in a school, we not only need to distinguish students and assign them student numbers, but also need to use employee numbers to distinguish faculty and staff.
The addThing function is used to define things, and its type signature is as follows:
`ts
export declare function addThing(thingName: string, begin?: number): boolean;
`
- It accepts a string parameter (thingName) to identify the thing. For example:
`ts
import { addThing } from ''@jcstdio/jc-utils/id'
addThing('student'); // Adding student as a thing is equivalent to creating a new classification of student.
addThing('staff'); // Adding staff as a thing is equivalent to creating a new classification of staff.
`
- Another numerical parameter (begin) is used to specify the internally started count value, which will increase with the number of applied IDs, starting from 0 by default.
> Note: Things defined by using the addThing function in any module cannot be repeated.
$3
Suppose a new student reports to the school. In order not to repeat the student number, we need to check the current student number position, then register a new student number for the new student and tell the student back. You can apply for a new ID by using the getNewItemId function. The type signature of this function is:
`ts
export declare function getNewItemId(thingName: string): string;
`
For example, apply for a new ID for students' classification as a new student number:
`ts
import { getNewItemId } from '@jcstdio/jc-utils/id'
const id = getNewItemId('student');
`
> By default, an ID string encoded by Base64 is returned.
$3
You can try other APIs by referring to the following types of signatures:
`ts
declare type IDType = 'number' | 'simple' | 'baseb4';
/*Sets the type of return ID. /
export declare function setIdType(tp: IDType): void;
/*Gets the type of return ID (for viewing) /
export declare function getIdType(): IDType;
/*Create new things /
export declare function addThing(thingName: string, begin?: number): boolean;
/*Delete things /
export declare function dropThing(thingName: string): boolean;
/*Apply for a new ID for the specified thing. /
export declare function getNewItemId(thingName: string): string;
/*Get the current ID of something. /
export declare function getCurrentId(thingName: string): string;
/*Get the number of ids that something has given at present. /
export declare function countIds(thingName: string): number;
`
exception tools
`ts
import { ValueError, InputTypeError } from '@jcstdio/jc-utils'
// ValueError is a function return a instance of Error
throw ValueError('Value Error.')
`
noop/pass
`js
import { noop, pass } from '@jcstdio/jc-utils'
noop(); // do nothing
pass(); // do nothing
pass('hello','jack') // will print hello jack
`
type conversion
`js
import { str, int } from '@jcstdio/jc-utils'
str(1); // '1'
int('1'); // 1
`
animate tools
$3
This set of tools includes: slideUp、slideDown、verticalSlideToggle、slideLeft、slideRight、horizontalSlideToggle。
Their type signatures are:
- Type signature of slideUp:
`ts
/**
* Hide elements in a sliding way (imitating JQuery)
*
* @param element An Html element
* @param duration Time to complete animation
* @param targetHeight The height to be animated is 0 by default.
*/
declare function slideUp(element: HTMLElement, duration?: number, targetHeight?: number, removeStyleAfterFinished?: boolean): void;
/**
* Show all hidden in a sliding way (imitating JQuery)
*
* @param element An Html element
* @param duration Time to complete animation
* @param removeStyleAfterFinished Whether to remove inline style tags after slideDown execution is completed. Since the slideToggle series functions are implemented by injecting inline style, removing the style attribute can restore the side effects caused by the SlideToggle series functions.
*/
declare function slideDown(element: HTMLElement, duration?: number, removeStyleAfterFinished?: boolean): void;
/**
* Switch between slideUp and slideDown.
*
* That is, by checking the current height of the element and calling slideUp or slideDown accordingly.
*
* @param element An Html element
* @param duration The time for an animation (slideUp or slideDown) is 200ms by default.
* @returns
* - true If the slideUp is finished (meaning that the previous height is greater than 0, the upward closing is performed)
* - false If the slideDown is finished (which means opening down is performed)
*/
declare function verticalSlideToggle(element: HTMLElement, duration?: number): boolean;
/**
* Switch between slideUp and slideDown.
*
* @deprecated This function has been renamed verticalSlideToggle and may be deleted at any time. Because there is also a new horizontalSlideToggle function to switch between slideLeft and slideRight.
*/
declare function slideToggle(element: HTMLElement, duration?: number): void;
/**
* Hide elements to the left in a sliding manner.
*
*
* @param element An Html element
* @param duration Time to complete animation
* @param targetWidth The width to be animated is 0 by default.
* @param removeStyleAfterFinished Only used when slideLeft is used for restoring (generally speaking, during the Toggle period, one of slideLeft and slideRight is used for restoring), and the default value is false.
*/
declare function slideLeft(element: HTMLElement, duration?: number, targetWidth?: number, removeStyleAfterFinished?: boolean): void;
/**
* Slide all hidden elements to the right.
*
* @param element An Html element
* @param duration Time to complete animation
* @param targetWidth Width when opened, the default is full width.
* @param removeStyleAfterFinished Only used when slideRight is used for restore (generally speaking, during the Toggle period, one of slideLeft and slideRight is used for restore), and the default value is false.
*/
declare function slideRight(element: HTMLElement, duration?: number, targetWidth?: number | null, removeStyleAfterFinished?: boolean): void;
/**
* Switch between slideLeft and slideRight. That is, by checking the current width of the element and calling slideLeft or slideRight accordingly.
* @param element An Html element
* @param duration The time for an animation (executing slideLeft or SlideEFT) is 200ms by default.
* @param targetWidth The target width of slideRight when it is opened, and the default is full width.
* @returns Returns a boolean value that represents whether the element is closed or open after switching:
* - true: Close, that is, slideLeft is executed.
* - false: Open, that is, slideRight is executed.
*/
declare function horizontalSlideToggle(element: HTMLElement, duration?: number, targetWidth?: number | null, restoreBy?: 'Left' | 'Right'): boolean;
`
> Note: The slideToggle function of the old version (before v0.0.15) has been migrated to verticalSlideToggle and may be removed at any time.
For example:
`html
callBack()" ref="ulRef">
- 1
- 2
- 3
- 4
- 5
`
logger tools
`ts
import { Logger } from '@jcstdio/jc-utils'
const logger = new Logger('')
logger.trace('trace')
logger.debug('debug')
logger.info('info')
logger.success('success')
logger.warn('warn')
logger.error('error')
logger.fatal('fatal')
logger.level1('level1')
logger.level2('level2')
logger.level3('level3')
logger.gradient('geadientText')
`
!logger.png
math tools
$3
Used to construct a arithmetic progression array.
`ts
import { arithmeticProgression } from '@jcstdio/jc-utils'
arithmeticProgression(1,9,3); // [ 1, 3, 5, 7, 9 ]
arithmeticProgression(9,1,3); // [ 9, 7, 5, 3, 1 ]
arithmeticProgression(-9,-1,3); // [ -9, -7, -5, -3, -1 ]
arithmeticProgression(-1,-9,3); // [ -1, -3, -5, -7, -9 ]
arithmeticProgression(-1,-9,0); // [ -1, -9 ]
arithmeticProgression(1,1,3); // [ 1, 1, 1, 1, 1 ]
`
$3
This tool is inspired by the range function provided in Python. In this module, range is a series of overloaded sequence generators that return an array.
The type signature of the range tool is as follows:
`ts
declare function range(x: number): number[];
declare function range(x: [number]): number[];
declare function range(x: [number, number]): number[];
declare function range(x: [number, number, number]): number[];
`
For example:
`ts
import { range } from '@jcstdio/jc-utils'
range([0, 3]); // [0,1,2]
range(3); // Equivalent to range([0, 3]), [0,1,2]
range([3]); // Equivalent to range(3), [0,1,2]
range([4, 8, 1]); // [4, 5, 6, 7], where the last number indicates the interval.
range([4, 8, 2]); // [4, 6]
range([4, 8]); // Equivalent to range([4, 8, 1]), [4, 5, 6, 7]
`
vite tools
$3
Filter all environment variables starting with VITE.
`ts
declare type filter = {
test: string | RegExp;
handle: (env: string) => any;
};
declare function getViteEnvs(envConf: Recordable, filters?: filter[]): T;
`
config tool
$3
`ts
import { IniConfig } from '@jcstdio/jc-utils'
`
Type declaration of class IniConfig is:
`ts
declare class IniConfig {
private _filePath;
private _conf;
constructor(_filePath?: string);
get value(): Record>;
load(obj: Record>): void;
readSync(filePath?: string): this;
static format>>(obj: U): string;
private _farmat;
writeSync(filePath?: string): void;
addSection>(section: string, records: V): this;
addRecord(section: string, key: string, value: V): this;
getSection(section?: string): Record;
get(section?: string, key?: string): any;
private __str__;
print(): this;
}
`
$3
`ts
import { getEnvs } from '@jcstdio/jc-utils'
`
Type declaration of function getEnvs is:
`ts
declare function getEnvs(envFilePath: string, regExp?: RegExp, parse?: (right: string) => T): Record;
`
cache tools
$3
`ts
import { Cookies } from '@jcstdio/jc-utils'
let cookies = new Cookies();
`
$3
$3
`ts
import { List } from '@jcstdio/jc-utils'
`
$3
A data structure inspired by pandas (a python package).
`ts
import { Series } from '@jcstdio/jc-utils'
`
examples:
`ts
import { Series } from '@jcstdio/jc-utils'
// Create a Series object.
const s = new Series([1, 2, 3, 4], { index: ['a', 'b', 'c', 'd'] });
// Use the map method to add 1 to each element in the Series.
const s1 = s.map((val: number) => val + 1);
s1.print()
// Use the dropNa method to delete NaN values in Series.
const s2 = new Series([1, 2, NaN, 4], { index: ['a', 'b', 'c', 'd'] });
s2.print()
const s3 = s2.dropNa();
s3.print()
// Use the fillNa method to replace the NaN value in Series with 0.
const s4 = new Series([1, 2, NaN, 4], { index: ['a', 'b', 'c', 'd'] });
s4.print()
const s5 = s4.fillNa({ value: 0 });
s5.print()
// Using isNa method to detect whether there is NaN value in Series.
const s6 = new Series([1, 2, NaN, 4], { index: ['a', 'b', 'c', 'd'] });
s6.print()
const hasNa = s6.isNa();
// Use the replace method to replace the specified value in Series with a new value.
const s7 = new Series([1, 2, 3, 4], { index: ['a', 'b', 'c', 'd'] });
s7.print()
const s8 = s7.replace(2, 5);
s8.print()
// Use the or method to return the logical or of two Series.
const s9 = new Series([true, false, true, false], { index: ['a', 'b', 'c', 'd'] });
s9.print()
const s10 = new Series([false, true, false, true], { index: ['a', 'b', 'c', 'd'] });
s10.print()
const s11 = s9.or(s10);
s11.print()
// Use the and method to return the logical sum of two Series.
const s12 = new Series([true, false, true, false], { index: ['a', 'b', 'c', 'd'] });
s12.print()
const s13 = new Series([false, true, false, true], { index: ['a', 'b', 'c', 'd'] });
s13.print()
const s14 = s12.and(s13);
s14.print()
// Use argSort method to return the index sorted by Series.
const s15 = new Series([3, 1, 4, 2], { index: ['a', 'b', 'c', 'd'] });
s15.print()
const sortedIndex = s15.argSort({ ascending: true });
console.log('sortedIndex =',sortedIndex);
// Use the argMin method to return the index of the smallest value in the Series.
const s16 = new Series([3, 1, 4, 2], { index: ['a', 'b', 'c', 'd'] });
s16.print()
const minIndex = s16.argMin();
console.log('minIndex =',minIndex)
// Use the argMax method to return the index of the largest value in the Series.
const s17 = new Series([3, 1, 4, 2], { index: ['a', 'b', 'c', 'd'] });
s17.print()
const maxIndex = s17.argMax({ ascending: true });
console.log('maxIndex =',maxIndex)
// Use the sortValues method to return the new Series sorted by Series.
const s18 = new Series([3, 1, 4, 2], { index: ['a', 'b', 'c', 'd'] });
s18.print()
const sortedSeries = s18.sortValues({ ascending: true });
s18.print('sortedSeries =',sortedSeries)
// Print Series using the print method.
const s19 = new Series([1, 2, 3, 4], { index: ['a', 'b', 'c', 'd'] });
s19.print()
`
copy tools
Provides tools for shallow and deep copying of JavaScript objects.
`
import { copy } from '@jcstdio/jc-utils'
// Shallow copy
const obj1 = {a: 1, b: {c: 2}};
const shallowCopyObj1 = copy.shallowCopy(obj1);
console.log(shallowCopyObj1); // {a: 1, b: {c: 2}}
console.log(shallowCopyObj1.b === obj1.b); // true
// DeepCopy realized by deepcopy method (recursive replication nested object implementation)
const obj2 = {a: 1, b: {c: 2}};
const deepCopyObj2 = copy.deepCopy(obj2);
console.log(deepCopyObj2); // {a: 1, b: {c: 2}}
console.log(deepCopyObj2.b === obj2.b); // false
// Deep copy realized by serializeCopy method (realized by JSON serialization and deserialization)
const obj3 = {a: 1, b: {c: 2}};
const serializeCopyObj3 = copy.serializeCopy(obj3);
console.log(serializeCopyObj3); // {a: 1, b: {c: 2}}
console.log(serializeCopyObj3.b === obj3.b); // false
`
Codec tool
$3
This section provides two tools, strEncodeBase64 and strBase64Decode, which are used for Base64 encoding and restoring of strings respectively. Their type signatures are as follows:
> since v0.0.17
- strEncodeBase64
`ts
/**
* Base64 encode a string.
* @param str A string
* @returns Base64 encoded string.
*/
export declare function strEncodeBase64(str: string): string;
`
- strBase64Decode
`ts
/**
* Restores a string encoded by Base64 to the original string.
* @param str A string
* @returns Base64 Decoded string
*/
export declare function strBase64Decode(str: string): string;
`
Proxy and Reactive Tools
$3
#### Agent creator of Array
The createArrayProxy function is used to create the proxy of Array, and its function is more powerful than the original Array. For example:
1. The array after proxy can be indexed negatively.
2. Delete the element at the specified index without leaving any space.
-You can use either the delete keyword or the extended drop method. However, it should be pointed out that this drop method exists only on proxy objects.
3. Automatically assign an event to the agent's object every time the function is called, and you can specify the side effects of the event to realize responsive data.
The type signature of the createArrayProxy tool is as follows:
`ts
declare type printType = 'log' | 'json';
declare type ArrayProxy = Array & {
drop: (index: number) => void;
print: (type?: printType) => any;
};
/**
* Create a proxy for Array
* - Negative numbers can be indexed.
* - Delete the element at the specified index without leaving a space.
* - An event is automatically assigned to the agent's object every time a function is called, and the side effects of the event can be specified to realize responsive data.
* @param target The target object of the proxy
* @returns Object after proxy
*/
declare function createArrayProxy(target: Array, sideEffect?: (...params: any[]) => any): ArrayProxy;
`
For example:
`ts
import { createArrayProxy } from './dist';
let arProxy1 = createArrayProxy([0,1,2,'3'])
console.log('arProxy1 =',arProxy1);
console.log('---------- 测试索引 ----------');
console.log('ar[1] =',arProxy1[1]);
console.log('ar[0] =',arProxy1[0]);
console.log('ar[0] =',arProxy1[-1]);
console.log('ar[-1] =',arProxy1[-1]);
console.log('ar[-2] =',arProxy1[-2]);
console.log('ar[-3] =',arProxy1[-3]);
console.log('---------- 测试length属性 ----------');
console.log('.length =',arProxy1.length);
console.log('---------- 测试 pop、push 等方法 ----------');
arProxy1.pop()
console.log('ar.pop() =',arProxy1);
arProxy1.push(3)
console.log('ar.push(3) =',arProxy1);
arProxy1.forEach((item)=>{
console.log('item =',item);
})
console.log('---------- 测试 delete ----------');
console.log('delete arProxy1[1]:');
delete arProxy1[1]
console.log(arProxy1);
console.log('---------- 测试扩展的方法 ----------');
arProxy1.drop(2);
console.log('ar.drop(2) =',arProxy1);
console.log('print方法输出 arProxy2:');
const arProxy2 = createArrayProxy([
0,
[1,2,3,{}],
{a:1,b:[1,2,3]},
3
])
arProxy2.print()
console.log('---------- 测试 delete ----------');
console.log('delete arProxy2[1]:');
delete arProxy2[1]
console.log(arProxy2);
`
OutPuts:
`console
arProxy1 = [ 0, 1, 2, '3' ]
---------- 测试索引 ----------
ar[1] = 1
ar[0] = 0
ar[0] = 3
ar[-1] = 3
ar[-2] = 2
ar[-3] = 1
---------- 测试length属性 ----------
.length = 4
---------- 测试 pop、push 等方法 ----------
ar.pop() = [ 0, 1, 2 ]
ar.push(3) = [ 0, 1, 2, 3 ]
---------- 测试迭代器 ----------
item = 0
item = 1
item = 2
item = 3
---------- 测试 delete ----------
delete arProxy1[1]:
[ 0, 2, 3 ]
---------- 测试扩展的方法 ----------
ar.drop(2) = [ 0, 2 ]
print方法输出 arProxy2:
[ 0, [ 1, 2, 3, {} ], { a: 1, b: [ 1, 2, 3 ] }, 3 ]
---------- 测试 delete ----------
delete arProxy2[1]:
[ 0, { a: 1, b: [ 1, 2, 3 ] }, 3 ]
`
#### Agent creator of Set
> Not yet completed
#### Agent creator of Map
> Not yet completed
Type Tools
A set of tools for TypeScript Type.Type signatures of all types of tools are as follows:
export from: @jcstdio/jc-utils/ts, you must to import from '@jcstdio/jc-utils/ts', for example:
`ts
import type { RequiredKeys, Recordable } from '@jcstdio/jc-utils/ts'
``