Commit 368f4ff1 authored by nanahira's avatar nanahira

auto type infer

parent d92737fe
Pipeline #6286 passed with stages
in 1 minute and 24 seconds
# Koishi Utils Schemagen
# koishi-utils-schemagen
在 Koishi.js 中,使用类装饰器定义 Schema。
......@@ -23,14 +23,18 @@ export class Config {
@DefineSchema({ type: 'number', required: true })
foo: number;
// 非数组类型会尝试自动推断类型
@DefineSchema()
fooAutomated: number;
@DefineSchema({ type: 'string', default: 'shigma' })
bar: string;
@DefineSchema({ type: 'boolean', default: true, hidden: true })
baz: boolean;
// 数组
@DefineSchema({ type: 'string', array: true, default: ['foo', 'bar'] })
// 数组,系统会自动推断该类型为数组,但是此时 type 不可省略
@DefineSchema({ type: 'string', default: ['foo', 'bar'] })
ant: string[];
// 也可以用定义好的 Schema
......@@ -49,7 +53,7 @@ export class Config {
@ObjectSchema(B)
anotherB: B;
// 字典
// 字典,type 也不可省略
@DefineSchema({ type: B, dict: true })
biDict: Record<string, B>;
......@@ -66,4 +70,8 @@ export class Config {
const schema = schemaFromClass(Config);
// 直接获取 Config 对象并实例化,可以代替 Schema.validate 使用。对于嵌套类会进行循环实例化。
const config = schemaTransform(Config, someObject);
```
\ No newline at end of file
```
### 类型推断
koishi-utils-schemagen 会尝试自动从类定义推断 Schema 类型,但是无法自动推断数组和字典类型。
export * from './src/def';
export * from './src/decorators';
export * from './src/methods';
{
"name": "koishi-utils-schemagen",
"version": "1.0.7",
"version": "1.1.0",
"description": "在 Koishi.js 中,使用类装饰器定义 Schema",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {
"lint": "eslint --fix .",
"build": "tsc"
......
......@@ -20,13 +20,48 @@ function transformDict<T>(cl: ClassConstructor<T>, val: any, array: boolean) {
}
}
export function DefineSchema(options: SchemaOptions): PropertyDecorator {
function getStringFromNativeType(nativeType: any) {
if (!nativeType) {
return;
}
const nativeTypeString = nativeType.toString() as string;
if (!nativeTypeString) {
return;
}
if (nativeTypeString.startsWith('class')) {
return 'class';
}
if (!nativeTypeString.startsWith('function ')) {
return;
}
const firstLeftBracketPos = nativeTypeString.indexOf('()');
if (firstLeftBracketPos === -1) {
return;
}
const typeString = nativeTypeString.slice(9, firstLeftBracketPos);
return typeString.toLowerCase();
}
export function DefineSchema(options: SchemaOptions = {}): PropertyDecorator {
return (obj, key) => {
const objClass = obj.constructor;
const keys: string[] =
Reflect.getMetadata(SchemaKeysMetaKey, objClass) || [];
keys.push(key.toString());
Reflect.defineMetadata(SchemaKeysMetaKey, keys, objClass);
const nativeType = Reflect.getMetadata('design:type', obj, key);
const nativeTypeString = getStringFromNativeType(nativeType);
if (!options.type) {
if (nativeTypeString && nativeTypeString !== 'array') {
options.type =
nativeTypeString === 'class' ? nativeType : nativeTypeString;
} else {
options.type = 'any';
}
}
if (nativeTypeString === 'array') {
options.array = true;
}
Reflect.defineMetadata(SchemaMetaKey, options, objClass, key);
if (options.type && typeof options.type !== 'string') {
const cl = options.type as ClassConstructor<any>;
......
......@@ -5,6 +5,7 @@ export type SchemaType =
| 'string'
| 'number'
| 'boolean'
| 'object'
| 'any'
| 'never'
| ClassConstructor<any>;
......
export * from './def';
export * from './decorators';
export * from './methods';
......@@ -27,6 +27,8 @@ function getBasePropertySchemaFromOptions(options: SchemaOptions) {
return Schema.number();
case 'boolean':
return Schema.boolean();
case 'object':
return Schema.object({}, true);
default:
return Schema.any();
}
......
import 'reflect-metadata';
function LogType(): PropertyDecorator {
return (target, key) => {
const t = Reflect.getMetadata('design:type', target, key);
console.log(key, t, typeof t, t.toString());
};
}
class B<T> {
foo: T;
}
class A {
@LogType()
foo: string;
@LogType()
bar: number;
@LogType()
a: Date;
@LogType()
m: Map<string, any>;
@LogType()
s: Set<any>;
@LogType()
b: B<string>;
@LogType()
c: boolean;
@LogType()
d: B<any>[];
@LogType()
aaa: any;
}
......@@ -3,17 +3,17 @@ import {
SchemaConf,
schemaFromClass,
schemaTransform,
} from '../src';
} from '..';
import { Schema } from 'koishi';
@SchemaConf({
desc: 'my desc',
})
class B {
@DefineSchema({ type: 'number', default: 2, desc: 'aaaaa' })
@DefineSchema({ default: 2, desc: 'aaaaa' })
aa: number;
@DefineSchema({ type: 'boolean', default: true })
@DefineSchema({ default: true })
bb: boolean;
}
......@@ -21,22 +21,22 @@ class B {
desc: 'my base desc',
})
class A {
@DefineSchema({ type: 'number', required: true })
@DefineSchema({ required: true })
a: number;
@DefineSchema({ type: 'string', default: 'shigma' })
@DefineSchema({ default: 'shigma' })
b: string;
@DefineSchema({ type: 'string', array: true, default: ['foo', 'bar'] })
@DefineSchema({ type: 'string', default: ['foo', 'bar'] })
c: string[];
@DefineSchema({ type: B })
@DefineSchema()
bi: B;
@DefineSchema({ type: B, array: true })
@DefineSchema({ type: B })
biArr: B[];
@DefineSchema({ type: B, dict: true, array: true })
@DefineSchema({ type: B, dict: true })
biDict: Record<string, B>[];
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment