ts-ref-kit
v1.2.0
Published
Type reflection and validation library for TypeScript
Readme
ts-ref-kit
TypeScript type reflection and validation library for runtime access and usage of TypeScript type information.
Features
- Type Reflection: Retrieve metadata for TypeScript classes, interfaces, type aliases, and enums at runtime
- Type Validation: Validate whether values conform to specific TypeScript type definitions
- JSON Conversion: Convert JSON strings to class instances
- Type Queries: Query inheritance relationships, interface implementations, and more between classes
- Generic Support: Support for reflection and validation of generic types
- Enum Handling: Access enum names, values, and type definitions
Installation
npm install ts-ref-kitQuick Start
Type Reflection Example
import { getClassDefinition, getInterfaceDefinition, getTypeAliasDefinition, getEnumDefinition } from 'ts-ref-kit'
// Get class definition
const classDef = getClassDefinition('MyClass')
console.log(classDef?.name) // Output: class name
console.log(classDef?.properties) // Output: class properties list
console.log(classDef?.methods) // Output: class methods list
// Get interface definition
const interfaceDef = getInterfaceDefinition('MyInterface')
console.log(interfaceDef?.name) // Output: interface name
console.log(interfaceDef?.properties) // Output: interface properties list
console.log(interfaceDef?.methods) // Output: interface methods list
// Get type alias definition
const typeAliasDef = getTypeAliasDefinition('MyTypeAlias')
console.log(typeAliasDef?.name) // Output: type alias name
console.log(typeAliasDef?.type) // Output: type definition
// Get enum definition
const enumDef = getEnumDefinition('MyEnum')
console.log(enumDef?.name) // Output: enum name
console.log(enumDef?.members) // Output: enum membersType Validation Example
import { isType, assertType } from 'ts-ref-kit'
// Validate if value matches type
const value = 'test'
const isString = isType(value, 'string')
console.log(isString) // Output: true
// Assert value matches type (throws error if not)
assertType<string>(value, 'string')
// Validate a reflected interface
interface User {
name: string
age: number
}
const user = { name: 'John', age: 30 }
const isUser = isType(user, 'User')
console.log(isUser) // Output: trueJSON Conversion Example
import { JSONTransfer } from 'ts-ref-kit'
class User {
name: string
age: number
constructor() {
this.name = ''
this.age = 0
}
}
const jsonString = '{"name":"John","age":30}'
const jsonTransfer = new JSONTransfer()
// Convert JSON string to class instance
const user = jsonTransfer.parse<User>(jsonString, 'User')
console.log(user instanceof User) // Output: true
console.log(user.name) // Output: John
console.log(user.age) // Output: 30Enum Operations Example
import { getEnumNames, getEnumValues } from 'ts-ref-kit'
enum Color {
Red,
Green,
Blue
}
// Get enum names list
const colorNames = getEnumNames('Color')
console.log(colorNames) // Output: ['Red', 'Green', 'Blue']
// Get enum values list
const colorValues = getEnumValues({ Color })
console.log(colorValues) // Output: [0, 1, 2]API Documentation
Core Type Definitions
- TypeDefinition: Core interface representing TypeScript types
- ClassDefinition: Definition for class types
- InterfaceDefinition: Definition for interface types
- TypeAliasDefinition: Definition for type aliases
- EnumDefinition: Definition for enum types
Type Reflection API
Class Related
getClassDefinition(arg: Constructor | object | string): ClassDefinition | undefinedgetClassByName(className: string): Constructor | undefined
Interface Related
getInterfaceDefinition(name: string): InterfaceDefinition | undefined
Type Alias Related
getTypeAliasDefinition(name: string, genericArgs?: TypeDefinition[]): TypeAliasDefinition | undefined
Enum Related
getEnumDefinition(name: string): EnumDefinition | undefinedgetEnumNames(enumName: string): string[]getEnumValues(args: { [enumName: string]: Record<string, string | number> }): (string | number)[]
Type Utilities
getTypeDef(type: Type | TypeDefinition): TypeDefinitionTypeDefinition.isPrimitiveType(type: TypeDefinition): booleanTypeDefinition.isArrayType(type: TypeDefinition): booleanTypeDefinition.isLiteralType(type: TypeDefinition): booleanTypeDefinition.isTypeLiteralType(type: TypeDefinition): booleanTypeDefinition.isUnionType(type: TypeDefinition): booleanTypeDefinition.isIntersectionType(type: TypeDefinition): booleanTypeDefinition.isTupleType(type: TypeDefinition): booleanTypeDefinition.isClassDefinition(type: TypeDefinition): booleanTypeDefinition.hasGenerics(type: TypeDefinition): boolean
Type Validation API
isType(value: unknown, type: Type | TypeDefinition, depth: number = 5): booleanassertType<T>(data: unknown, type: Type, depth: number = 5): TvalidateValue(value: unknown, typeDef: TypeDefinition, depth: number = 1): ValidateResult
JSON Conversion API
JSONTransferclassparse<T = unknown>(jsonString: JSONString<T>, type?: Type): T
Configuration Options
import { ReflectConfig } from 'ts-ref-kit'
// Validation error handler
ReflectConfig.validationErrorHandler = e => {
console.error(e.message)
console.error(e.failureResult?.errorStackFlow)
}
// Whether to enable validation
ReflectConfig.validation = true
// Whether to enable debug mode
ReflectConfig.debug = falseAdvanced Usage
Generic Type Handling
import { getTypeDef } from 'ts-ref-kit'
// Get a generic type definition
const mapType = getTypeDef('Map<string, number>')
console.log(mapType.name) // Output: Map
console.log(mapType.generics?.[0].name) // Output: string
console.log(mapType.generics?.[1].name) // Output: number
// Get a reflected type by name
const userType = getTypeDef('User')
console.log(userType.classDefinition?.name || userType.interfaceName || userType.typeAliasName)Type Relationship Queries
import { getClassDefinition } from 'ts-ref-kit'
// Query if a class is a subclass of another class
const classDef = getClassDefinition('MyClass')
const isSubClass = classDef?.isSubClassOf('BaseClass')
// Query if a class is a superclass of another class
const isSuperClass = classDef?.isSuperClassOf('DerivedClass')Special Type Registration
import { registerSpecialType } from 'ts-ref-kit'
// Register custom special type
registerSpecialType({
name: 'MySpecialType',
genericParams: ['T'],
getType(typeArg) {
return typeArg
}
})Parser Configuration
The core functionality of ts-ref-kit relies on type metadata generated at compile time. The parser module provides integration capabilities with different build tools to extract type information and generate metadata during compilation.
Vite Plugin
In Vite projects, you can use reflectParserPlugin to automatically generate type metadata:
import { defineConfig } from 'vite'
import { reflectParserPlugin } from 'ts-ref-kit/parser'
export default defineConfig({
plugins: [
reflectParserPlugin({
entry: 'src/main.ts',
sourcePaths: 'src'
})
]
})When multiple independently built projects run in the same browser or Node.js
context, assign each project a separate reflect module. default is only safe
for a single project/build output; if multiple outputs all write metadata to
default, the last one loaded may overwrite the others.
// vite.config.ts
reflectParserPlugin({
entry: 'src/main.ts',
sourcePaths: 'src',
reflectModule: 'project-a'
})Shared runtime usage
// src/reflect.ts
import { getReflectModule } from 'ts-ref-kit'
export const reflect = getReflectModule('project-a')
export const ReflectClass = reflect.ReflectClass
export const ReflectValidate = reflect.ReflectValidateimport { reflect } from './reflect'
reflect.getClassDefinition('User')
reflect.isType(user, 'User')Use this style when several packages share the same ts-ref-kit runtime, for
example when libraries depend on the application's installed copy. In this
scenario, avoid setReflectModule for isolation: it changes the shared default
module name and can affect other packages using the same runtime.
Bundled runtime compatibility
If a package bundles its own copy of ts-ref-kit, its default module state is
local to that bundled copy. Existing code that uses top-level APIs can be kept by
setting the module once at package startup:
// package entry
import { setReflectModule } from 'ts-ref-kit'
setReflectModule('project-a')After that, top-level APIs in that bundled package use project-a:
import { getClassDefinition, isType } from 'ts-ref-kit'
getClassDefinition('User')
isType(user, 'User')This compatibility style is intended for bundled/inline runtimes. For shared
runtimes, prefer the explicit getReflectModule(name) API shown above.
Custom Build Configuration
If you're using other build tools or a custom build process, you can directly use reflectLoader:
import { reflectLoader } from 'ts-ref-kit/parser'
const loader = reflectLoader({
sourcePaths: 'src',
reflectModule: 'project-a',
exclude: /node_modules/,
outputLog: true
})
// Transform a single file
const transformedCode = loader.transform(sourceCode, sourceFileName)
// Generate metadata injection code for the entry module
const metadataCode = loader.outputAllMetas()Parser Options
entry: Entry file path of the projectsourcePaths: Source code paths to process, can be a string or array of stringsexclude: File paths to exclude, can be a string, regular expression, or array of themforEnabledClassOnly: Whether to only process classes that implement theEnableReflectinterfaceoutputLog: Whether to output log informationreflectModule: Optional reflect module name for isolating metadata. Use a unique value for each independently built project/package that can run in the same browser or Node.js context.
Working Principle
- The parser scans TypeScript source code during compilation
- Extracts definitions of classes, interfaces, enums, and type aliases
- Generates type metadata and injects it into the compiled code
- Runtime access to this metadata is available through
ts-ref-kitAPIs
Node.js Environment Usage
In Node.js environments, you can directly use the parser to analyze TypeScript files and generate injection code:
const { reflectLoader } = require('ts-ref-kit/parser')
const fs = require('fs')
const sourceCode = fs.readFileSync('src/types.ts', 'utf8')
const loader = reflectLoader({
sourcePaths: 'src',
reflectModule: 'project-a'
})
const transformedCode = loader.transform(sourceCode, 'src/types.ts')
const metadataCode = loader.outputAllMetas()
console.log(metadataCode)You can also initialize reflection directly in Node.js with setupReflectTypes.
The reflectModule option is optional and defaults to default, but it should
match the runtime module name when you isolate multiple projects:
const { setupReflectTypes } = require('ts-ref-kit/parser')
const { getReflectModule } = require('ts-ref-kit')
await setupReflectTypes({
filePaths: ['src'],
sourceFolder: process.cwd(),
distFolder: process.cwd(),
reflectModule: 'project-a'
})
const reflect = getReflectModule('project-a')
const classDef = reflect.getClassDefinition('User')Development
Install Dependencies
npm installBuild Project
npm run buildRun Lint
npm run lintLicense
MIT
