UNPKG

typia

Version:

Superfast runtime validators with only one line

141 lines (127 loc) 3.9 kB
import ts from "typescript"; export namespace TypeFactory { export const isFunction = (type: ts.Type): boolean => getFunction(type) !== null; export const getFunction = (type: ts.Type) => { const node = type.symbol?.declarations?.[0]; if (node === undefined) return null; return ts.isFunctionLike(node) ? node : ts.isPropertyAssignment(node) || ts.isPropertyDeclaration(node) ? ts.isFunctionLike(node.initializer) ? node.initializer : null : null; }; export const getReturnTypeOfClassMethod = (props: { checker: ts.TypeChecker; class: ts.Type; function: string; }): ts.Type | null => { // FIND TO-JSON METHOD const symbol: ts.Symbol | undefined = props.class.getProperty( props.function, ); if (!symbol) return null; else if (!symbol.valueDeclaration) return null; // GET FUNCTION DECLARATION const functor: ts.Type = props.checker.getTypeOfSymbolAtLocation( symbol, symbol.valueDeclaration, ); // RETURNS THE RETURN-TYPE const signature: ts.Signature | undefined = props.checker.getSignaturesOfType(functor, ts.SignatureKind.Call)[0]; return signature ? signature.getReturnType() : null; }; export const getFullName = (props: { checker: ts.TypeChecker; type: ts.Type; symbol?: ts.Symbol; }): string => { // PRIMITIVE const symbol = props.symbol ?? props.type.aliasSymbol ?? props.type.getSymbol(); if (symbol === undefined) return props.checker.typeToString(props.type); // UNION OR INTERSECT if ( props.type.aliasSymbol === undefined && props.type.isUnionOrIntersection() ) { const joiner: string = props.type.isIntersection() ? " & " : " | "; return props.type.types .map((child) => getFullName({ checker: props.checker, type: child, }), ) .join(joiner); } //---- // SPECIALIZATION //---- const name: string = get_name(symbol); // CHECK GENERIC const generic: readonly ts.Type[] = props.type.aliasSymbol ? (props.type.aliasTypeArguments ?? []) : props.checker.getTypeArguments(props.type as ts.TypeReference); return generic.length ? name === "Promise" ? getFullName({ checker: props.checker, type: generic[0]!, }) : `${name}<${generic .map((child) => getFullName({ checker: props.checker, type: child, }), ) .join(", ")}>` : name; }; const explore_name = (props: { node: ts.Node; name: string }): string => ts.isModuleBlock(props.node) ? explore_name({ node: props.node.parent.parent, name: `${props.node.parent.name.getFullText().trim()}.${props.name}`, }) : props.name; const get_name = (symbol: ts.Symbol): string => { const parent = symbol.getDeclarations()?.[0]?.parent; return parent ? explore_name({ node: parent, name: symbol.escapedName.toString(), }) : "__type"; }; export const keyword = ( type: | "void" | "any" | "unknown" | "boolean" | "number" | "bigint" | "string", ) => { return ts.factory.createKeywordTypeNode( type === "void" ? ts.SyntaxKind.VoidKeyword : type === "any" ? ts.SyntaxKind.AnyKeyword : type === "unknown" ? ts.SyntaxKind.UnknownKeyword : type === "boolean" ? ts.SyntaxKind.BooleanKeyword : type === "number" ? ts.SyntaxKind.NumberKeyword : type === "bigint" ? ts.SyntaxKind.BigIntKeyword : ts.SyntaxKind.StringKeyword, ); }; }