Open majo44 opened 5 years ago
Typescript types declaration in rsuite https://github.com/rsuite/rsuite/blob/master/src/Schema/index.d.ts
Hi, thanks, good to know that there is declaration:
I very like that lib, have simple reasonable api, supports es6, and do not have any dependencies, I checked 20 different similar libs, and I very like to use that one in my project, and I do not using RSuite as I do not using the React there at all, so:
interface A {a: string, b?: number}
// Example of schema with provided type
const typedSchema = new Schema<A>({
a: NumberType(), // Error Type 'INumberType<Partial<A>>' is not assignable to type 'IType<string, Partial<A>>'... Type 'number' is not assignable to type 'string'.
// for autocast of data in addRule we can also add type here
b: NumberType<A>().isRequired('Required').addRule((value, data) => {
let v: string = value; // Error: Type 'number' is not assignable to type 'string'
let d: {x: any} = data; // Error: Property 'x' is missing in type 'A' but required in type '{ x: any; }'.
}),
c: BooleanType(), // Error: Argument of type '{ a: INumberType<any>; b: INumberType<A>; c: IBooleanType<any>; }' is not assignable to parameter of type 'ISchema<Partial<A>>'.
});
typedSchema.check({ c: 12}); //Error: Argument of type '{ c: number; }' is not assignable to parameter of type 'A'
typedSchema.checkForField('x', '12'); //Error: Argument of type '"x"' is not assignable to parameter of type '"a" | "b" | "c"'.
// Example of schema without provided type
// The validation object type will be inferred
const noTypedSchema = new Schema({
a: NumberType(),
b: NumberType().isRequired('Required').addRule((value, data) => {
let v: string = value; // Error: Type 'number' is not assignable to type 'string'
let d: {x: any} = data;
}),
c: BooleanType(),
});
noTypedSchema.check({ c: 12}); //Error: Type 'number' is not assignable to type 'boolean'.
noTypedSchema.checkForField('x', '12'); //Error: Argument of type '"x"' is not assignable to parameter of type '"a" | "b" | "c"'.
// Example of schema with any type provided
const anySchema = new Schema<any>({
a: NumberType(),
b: NumberType().isRequired('Required').addRule((value, data) => {
let v: string = value; // Error: Type 'number' is not assignable to type 'string'
let d: {x: any} = data;
}),
c: BooleanType(),
});
anySchema.check({ c: 12});
anySchema.checkForField('x', '12');
You can plat with that here
I created prototype of such declaration which allows such static checks:
export type RuleFn<V, D> = (value: V, data: D) =>
FieldSchemaCheckResult | boolean | void | undefined | Promise<boolean> | Promise<FieldSchemaCheckResult> | Promise<void>;
export interface IType<P, T> {
addRule(onValid: RuleFn<P, T>, errorMessage?: string, priority?: boolean): this;
}
export interface IStringType<T = any> extends IType<string, T> {
isRequired(errorMessage?: string, trim?: boolean): this;
isEmail(errorMessage?: string): this;
isURL(errorMessage?: string): this;
isOneOf(items: Array<string>, errorMessage?: string): this;
containsLetter(errorMessage?: string): this;
containsUppercaseLetter(errorMessage?: string): this;
containsLowercaseLetter(errorMessage?: string): this;
containsLetterOnly(errorMessage?: string): this;
containsNumber(errorMessage?: string): this;
pattern(regExp: RegExp, errorMessage?: string): this;
rangeLength(minLength: number, maxLength: number, errorMessage?: string): this;
minLength(minLength: number, errorMessage?: string): this;
maxLength(maxLength: number, errorMessage?: string): this;
}
export interface INumberType<T = any> extends IType<number, T> {
isRequired(errorMessage?: string): this;
isInteger(errorMessage?: string): this;
isOneOf(items: Array<number>, errorMessage?: string): this;
pattern(regExp: RegExp, errorMessage?: string): this;
range(minLength: number, maxLength: number, errorMessage?: string): this;
min(min: number, errorMessage?: string): this;
max(max: number, errorMessage?: string): this;
}
export interface IArrayType<I = any, T = any> extends IType<Array<I>, T> {
isRequired(errorMessage?: string): this;
rangeLength(minLength: number, maxLength: number, errorMessage?: string): this;
minLength(minLength: number, errorMessage?: string): this;
maxLength(maxLength: number, errorMessage?: string): this;
unrepeatable(errorMessage?: string): this;
of(type: IType<I, T>, errorMessage?: string): this;
}
export interface IDateType<T = any> extends IType<Date, T> {
isRequired(errorMessage?: string): this;
range(min: Date, max: Date, errorMessage?: string): this;
min(min: Date, errorMessage?: string): this;
max(max: Date, errorMessage?: string): this;
}
export interface IObjectType<O = any, T = any> extends IType<O, T> {
isRequired(errorMessage?: string): this;
shape(shape: ISchema<O>): this;
}
export interface IBooleanType<T = any> extends IType<boolean, T> {
isRequired(errorMessage?: string): this;
}
type IType<X, T> =
T extends any ? any :
X extends string ? IStringType<T>:
X extends number ? INumberType<T>:
X extends boolean ? IBooleanType<T>:
X extends Date ? IDateType<T>:
X extends Array<infer I> ? IArrayType<I, T>:
X extends any ?
IStringType<T> | INumberType<T> | IBooleanType<T> | IArrayType<any, T> | IDateType<T> | IObjectType<X, T> :
IObjectType<X, T>;
export type ISchema<T> = {
[P in keyof T]: IType<T[P], T>;
}
export type FieldSchemaCheckResult = {
hasError: boolean,
errorMessage: string
}
export type SchemaCheckResult<T> = {
[P in keyof T]: FieldSchemaCheckResult
};
export interface ISchemaModel<T = any> {
schema: ISchema<T>;
check(data: T): SchemaCheckResult<T>;
checkAsync(data: T): Promise<SchemaCheckResult<T>>;
checkForField<K extends keyof T>(fieldName: K, fieldValue: T[K], data?: T): FieldSchemaCheckResult;
checkForFieldAsync<K extends keyof T>(fieldName: K, fieldValue: T[K], data?: T): Promise<FieldSchemaCheckResult>;
getFieldType<K extends keyof T>(fieldName: K): IType<T[K], T>;
getKeys(): Array<PropertyKey>;
}
export interface ISchemaModelFactory {
<T>(schema: ISchema<Partial<T>>): ISchemaModel<T>;
combine<T>(...schemas: Array<ISchemaModel<Partial<T>>>): ISchemaModel<T>;
}
export declare const SchemaModel: ISchemaModelFactory;
export declare function StringType<T = any>(errorMessage?: string): IStringType<T>;
export declare function NumberType<T = any>(errorMessage?: string): INumberType<T>;
export declare function BooleanType<T = any>(errorMessage?: string): IBooleanType<T>;
export declare function ArrayType<I = any, T = any>(errorMessage?: string): IArrayType<I, T>;
export declare function DateType<T = any>(errorMessage?: string): IDateType<T>;
export declare function ObjectType<O = any, T = any>(errorMessage?: string): IObjectType<O, T>;
export declare class Schema<T = any> {
constructor(schema: ISchema<Partial<T>>);
check(data: T): SchemaCheckResult<T>;
checkAsync(data: T): Promise<SchemaCheckResult<T>>;
checkForField<K extends keyof T>(fieldName: K, fieldValue: T[K], data?: T): FieldSchemaCheckResult;
checkForFieldAsync<K extends keyof T>(fieldName: K, fieldValue: T[K], data?: T): Promise<FieldSchemaCheckResult>;
schema: ISchema<T>;
getFieldType<K extends keyof T>(fieldName: K): IType<T[K], T>;
getKeys(): Array<PropertyKey>;
}
schema-typed
should indeed have a separate typescript type definition.
If you like, I hope you can submit a PR.
As this becomes as an standard, and allows wider adoption, it is worth to add to library the typescript types declaration.