UNPKG

typia

Version:

Superfast runtime validators with only one line

221 lines (211 loc) • 4.94 kB
import ts from "typescript"; import { Metadata } from "../../../schemas/metadata/Metadata"; import { ArrayUtil } from "../../../utils/ArrayUtil"; import { TypeFactory } from "../../TypeFactory"; export const iterate_metadata_native = (checker: ts.TypeChecker) => (meta: Metadata, type: ts.Type): boolean => { const validator = validate(checker)(type); const name: string = TypeFactory.getFullName(checker)( type, type.getSymbol(), ); const simple = SIMPLES.get(name); if (simple && validator(simple)) { ArrayUtil.set(meta.natives, name, (str) => str); return true; } for (const generic of GENERICS) if ( name.substring(0, generic.name.length) === generic.name && validator(generic) ) { ArrayUtil.set(meta.natives, generic.name ?? name, (str) => str); return true; } return false; }; const validate = (checker: ts.TypeChecker) => (type: ts.Type) => (info: IClassInfo) => (info.methods ?? []).every((method) => { const returnType = TypeFactory.getReturnType(checker)(type)(method.name); return ( returnType !== null && checker.typeToString(returnType) === method.return ); }) && (info.properties ?? []).every((property) => { const prop = checker.getPropertyOfType(type, property.name); const propType = prop?.valueDeclaration ? checker.getTypeAtLocation(prop?.valueDeclaration) : undefined; return ( propType !== undefined && checker.typeToString(propType) === property.type ); }); const getBinaryProps = (className: string): IClassInfo => ({ name: className, methods: [ ...["indexOf", "lastIndexOf"].map((name) => ({ name, return: "number", })), ...["some", "every"].map((name) => ({ name, return: "boolean", })), ...["join", "toLocaleString"].map((name) => ({ name, return: "string", })), ...["reverse", "slice", "subarray"].map((name) => ({ name, return: className, })), ], properties: ["BYTES_PER_ELEMENT", "length", "byteLength", "byteOffset"].map( (name) => ({ name, type: "number", }), ), }); const SIMPLES: Map<string, IClassInfo> = new Map([ [ "Date", { methods: ["getTime", "getFullYear", "getMonth", "getMinutes"].map( (name) => ({ name, return: "number", }), ), }, ], [ "Boolean", { methods: [ { name: "valueOf", return: "boolean", }, ], }, ], [ "Number", { methods: [ ...["toFixed", "toExponential", "toPrecision"].map((name) => ({ name, return: "string", })), { name: "valueOf", return: "number" }, ], }, ], [ "String", { methods: [ "charAt", "concat", "valueOf", "trim", "replace", "substring", ].map((name) => ({ name, return: "string" })), }, ], ...[ "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", "BigUint64Array", "Int8Array", "Int16Array", "Int32Array", "BigInt64Array", "Float32Array", "Float64Array", ].map((name) => [name, getBinaryProps(name)] as const), ...["ArrayBuffer", "SharedArrayBuffer"].map((className) => { const info: IClassInfo = { methods: [{ name: "slice", return: className }], properties: [{ name: "byteLength", type: "number" }], }; return [className, info] as const; }), ...["Blob", "File"].map( (className) => [ className, { methods: [ { name: "arrayBuffer", return: "Promise<ArrayBuffer>" }, { name: "slice", return: "Blob" }, { name: "text", return: "Promise<string>" }, ], properties: [ { name: "size", type: "number" }, { name: "type", type: "string" }, ], }, ] satisfies [string, IClassInfo], ), [ "DataView", { methods: [ "getFloat32", "getFloat64", "getInt8", "getInt16", "getInt32", "getUint8", "getUint16", "getUint32", ].map((name) => ({ name, return: "number", })), }, ], [ "RegExp", { methods: [ { name: "test", return: "boolean", }, ], }, ], ]); const GENERICS: Array<IClassInfo & { name: string }> = [ "WeakMap", "WeakSet", ].map((name) => ({ name, methods: ["has", "delete"].map((name) => ({ name, return: "boolean", })), })); interface IClassInfo { name?: string; methods?: IMethod[]; properties?: IProperty[]; } interface IProperty { name: string; type: string; } interface IMethod { name: string; return: string; }