UNPKG

@matatbread/typia

Version:

Superfast runtime validators with only one line

255 lines (246 loc) 5.54 kB
import ts from "typescript"; import { MetadataNative } from "../../../schemas/metadata/MetadataNative"; import { ArrayUtil } from "../../../utils/ArrayUtil"; import { TypeFactory } from "../../TypeFactory"; import { IMetadataIteratorProps } from "./IMetadataIteratorProps"; export const iterate_metadata_native = ( props: IMetadataIteratorProps, ): boolean => { const name: string = TypeFactory.getFullName({ checker: props.checker, type: props.type, symbol: props.type.getSymbol(), }); const simple: IClassInfo | undefined = SIMPLES.get(name); if ( simple !== undefined && validate({ checker: props.checker, type: props.type, info: simple, }) ) { ArrayUtil.take( props.metadata.natives, (native) => native.name === name, () => MetadataNative.create({ name, tags: [], }), ); return true; } for (const generic of GENERICS) if ( name.substring(0, generic.name.length) === generic.name && validate({ checker: props.checker, type: props.type, info: generic, }) ) { ArrayUtil.take( props.metadata.natives, (native) => native.name === name, () => MetadataNative.create({ name: generic.name ?? name, tags: [], }), ); return true; } return false; }; const validate = (props: { checker: ts.TypeChecker; type: ts.Type; info: IClassInfo; }) => (props.info.methods ?? []).every((method) => { const returnType = TypeFactory.getReturnTypeOfClassMethod({ checker: props.checker, class: props.type, function: method.name, }); return ( returnType !== null && props.checker.typeToString(returnType) === method.return ); }) && (props.info.properties ?? []).every((property) => { const prop = props.checker.getPropertyOfType(props.type, property.name); const propType = prop?.valueDeclaration ? props.checker.getTypeAtLocation(prop?.valueDeclaration) : undefined; return ( propType !== undefined && props.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; }