English | 简体中文
Keeper is a library for securely accessing properties of js objects.
It generates a Keeper instance by receiving a string describing the object type. Through the API provided by this instance, we can access data of the expected safe type, or create a new object that fully complies with the type description for use.
Keeper also has excellent TypeScript support. It can generate corresponding type declaration files based on the received type description string, eliminating the need to manually create these files.
Example:
const userKeeper = createKeeper(`
name string
age int renamefrom:user_age
`);
// this type is {name: string, age: number}
const data = userKeeper.from({
name: "bruce",
user_age: "18.0",
});
console.log(data); // { name: 'bruce', age: 18 }
const age = userKeeper.read({ user_age: "18.2" }, "age"); // 18
npm i keeper-js
Keeper defines objects by receiving a string text that describes the object, which follows the format below:
<property> <type> <extentions>
<property>
: Property name, supporting strings or numbers.<type>
: Property type, which can be a basic type (such as string, int, float, see details below) or an array type (such as int[]). In addition, it also supports using*<extends>
format to implement nested types.<extensions>
(optional): Additional descriptions of the current property, currently supporting<copyas>:<alias>
(copy the current type as a new property named<alias>
) and<renamefrom>:<property>
(the current property value returns from the<property>
property of the source object).
Each description item is separated by one or more spaces, object descriptions support multiple lines, each line describes a property, and also supports comments with //. Example:
import { createKeeper } from "keeper-js";
const userInfo = createKeeper(`
name string
age int renamefrom:user_age
`);
const human = createKeeper(
`
id int
scores float[]
info *userInfo
`,
{ extends: { userInfo } },
); // Declare the inherited attributes of userInfo.
const data = human.from({
id: "1",
scores: ["80.1", "90"],
info: { name: "bruce", user_age: "18.0" },
});
// data: {
// id: 1,
// scores: [80.1, 90], // Transform string into float number.
// info: {
// name: 'bruce',
// age: 18, // Retrieve the value from 'user_age' and convert the float string into an integer number.
// }
// }
The Keeper instance provides two methods for data retrieval, from(obj)
and read(obj, path)
, which are used to generate a new object based on the type description and source object, and to get the value of the specified path in the source object according to the type description, respectively.
When we need to safely get a value from an object, we can use the read API to operate, for example:
const sourceData = {
id: "1",
scores: ["80.1", "90"],
info: { name: "bruce", user_age: "18.0" },
};
const name = human.read(sourceData, "id"); // 1
This method supports multi-layer nested access, for example:
const userInfo = createKeeper(`
name string
age int renamefrom:user_age
`);
const human = createKeeper(
`
id int
bros *userInfo[]
baseInfo *userInfo
`,
{ extends: { userInfo } },
); // Declare the inherited attributes of userInfo.
const sourceData = {
id: "1",
bros: [
{ name: "bro1", user_age: "16.0" },
{ name: "bro2", user_age: "17.2" },
],
info: { name: "bruce", user_age: "18.1" },
};
const name = human.read(sourceData, "info.name"); // 'bruce'
const bro1Name = human.read(sourceData, "bros[0].name"); // 'bro1'
When we expect to correct from the source data and get an object that fully conforms to the type declaration definition, we can use the from
API to operate, for example:
const sourceData = {
id: "1",
bros: [],
info: { name: "bruce", user_age: "18.1" },
};
human.from(sourceData); // { id: 1, bros: [], { name: 'bruce', age: 18 } }
Note that when the original data is empty and the corresponding declared property is not an empty type (null|undefined), a default value will be given according to the declared type, for example:
const sourceData = {
id: "1",
bros: [],
info: {},
};
human.from(sourceData); // { id: 1, bros: [], { name: '', age: 0 } }
human.read(sourceData, "bros[0].age"); // 0
Keeper has good ts support. You can get ts types from the defined keeper instance through the exported DefineKeeperInterface
type.
In addition, the from()
and read()
methods also have good ts support:
The second parameter of createKeeper
can be set to lazy
as true
. In this way, the Keeper will parse the type only at the first access, rather than parsing it at creation.
const userInfo = createKeeper(
`
name string
age int renamefrom:user_age
`,
{ lazy: true },
);
userInfo.properties.has("name"); // false
userInfo.get({ name: "bruce", user_age: "18.0" }, "name");
userInfo.properties.has("name"); // true
Data Type | JS Type | Default | Remarks |
---|---|---|---|
bool | boolean | false | - |
int | number | 0 | Integer type |
float | number | 0 | Floating point type |
string | string | '' | - |
null | null | null | - |
undefined | undefined | undefined | - |
func | Function | () => {} | - |
object | Object | {} | - |
Files: benchmark/index.js
result: benchmark.html lazy.api.html
MIT。