npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

farm-plugin-reflect

v0.2.4

Published

C#-style reflection for FarmFE — type_of<T>(), isOfType<T>(), union/intersection/type alias support, cross-package FQN registry

Readme

farm-plugin-reflect

C#-style reflection for FarmFE — type_of<T>() returns TypeInfo at runtime, isOfType<T>(obj) / isInterface<T>(obj) generate runtime type guards, classes implementing IObject auto-generate get_typeinfo(). Supports union types, intersection types, type aliases, and cross-package metadata sharing via a global runtime registry with FQN namespacing.

Why?

TypeScript types are erased at runtime. You can't inspect an interface's properties, check if an object conforms to a type, or enumerate enum members at runtime — until now. This plugin provides:

  1. Reflectiontype_of<T>() returns a rich TypeInfo object (like C#'s System.Type)
  2. Type guardsisOfType<T>(obj) checks if an object matches a type at runtime (interfaces, classes, enums, unions, intersections)
  3. IObject — classes implementing IObject auto-get get_typeinfo() method
  4. Cross-package sharing — reflection metadata is registered in a global singleton registry, keyed by FQN (pkg::Type), so types defined in one package can be used in another without re-generation

Install

pnpm add -D farm-plugin-reflect

Usage

1. Define types

// types.ts
export interface IUser {
  name: string
  age: number
  email?: string
  roles: string[]
}

export interface IAdmin extends IUser {
  permissions: string[]
}

export enum Role {
  Viewer = 0,
  Editor = 1,
  Admin = 2,
}

// Union types
export type Status = 'active' | 'inactive' | 'suspended'

// Intersection types
export type AdminUser = IUser & { permissions: string[] }

// Type aliases
export type UserId = string

2. Use type_of<T>() for runtime type info

// main.ts
import type { IUser, IAdmin, Role, Status, AdminUser } from './types'

const info = type_of<IUser>()
console.log(info.name)         // "IUser"
console.log(info.kind)         // "interface"
console.log(info.properties)   // [{ name: "name", type: TypeInfo(string), ... }, ...]
console.log(info.interfaces)    // [] (no extends)
console.log(info.isInterface)  // true

const adminInfo = type_of<IAdmin>()
console.log(adminInfo.interfaces)  // ["IUser"]

const roleInfo = type_of<Role>()
console.log(roleInfo.kind)     // "enum"

// Union and intersection types work too
const statusInfo = type_of<Status>()
console.log(statusInfo.kind)   // "union"

const adminUserInfo = type_of<AdminUser>()
console.log(adminUserInfo.kind) // "intersection"

3. Use isOfType<T>(obj) for runtime type guards

Works for interfaces, classes, enums, unions, and intersections:

const data: unknown = JSON.parse(input)

// Check against interface
if (isOfType<IUser>(data)) {
  console.log(data.name)   // ✅ type-safe
}

// Same as isInterface<T>() — alias for interface types
if (isInterface<IUser>(data)) {
  console.log(data.age)    // ✅ type-safe
}

// Check against enum
if (isOfType<Role>(value)) {
  // value is one of Role's values
}

// Check against class
if (isOfType<User>(obj)) {
  // obj is an instance of User
}

// Union type guard
if (isOfType<Status>(val)) {
  // val is 'active' | 'inactive' | 'suspended'
}

// Intersection type guard — all parts must match
if (isOfType<AdminUser>(obj)) {
  // obj matches IUser AND has permissions
}

4. Implement IObject for class instances

// user.ts
export class User implements IObject, IUser {
  name: string
  age: number
  email?: string
  roles: string[]

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
    this.roles = []
  }
  // get_typeinfo() is AUTO-GENERATED by the plugin
}

const user = new User('Alice', 30)
const info = user.get_typeinfo()
console.log(info.name)         // "User"
console.log(info.kind)         // "class"
console.log(info.properties)   // [{ name: "name", ... }, { name: "age", ... }, ...]
console.log(info.interfaces)    // ["IObject", "IUser"]
console.log(info.baseType)     // null

5. Configure FarmFE

// farm.config.ts
import { defineConfig } from '@farmfe/core'
import reflect from 'farm-plugin-reflect'

export default defineConfig({
  plugins: [reflect()],
})

Cross-Package Reflection

The Problem

If package @myorg/types defines IUser, and package @myorg/app calls type_of<IUser>(), the reflection metadata must be available in @myorg/app without re-generating it.

The Solution: FQN + Global Registry + reflect-usage

Every type gets a Fully Qualified Name (FQN) based on its package: @myorg/types::IUser. All TypeInfo and guards are registered in a global singleton registry (__reflectRegistry), ensuring the same instance is shared across all modules and packages.

Provider package (defines types)

  1. Add "reflect-usage": true to your package.json:
{
  "name": "@myorg/types",
  "reflect-usage": true
}
  1. The plugin automatically generates a reflect-register subpath export that registers all types from this package into the global registry.

Consumer package (uses types from another package)

The plugin auto-discovers dependencies with "reflect-usage": true in their package.json and imports their register files. No configuration needed — just use the types:

// @myorg/app — automatically finds @myorg/types' registry
import type { IUser } from '@myorg/types'

const info = type_of<IUser>()  // ✅ works — looks up "@myorg/types::IUser" in registry

FQN Format

| Package | Type | FQN | |---------|------|-----| | @myorg/types | IUser | @myorg/types::IUser | | @myorg/types | Role | @myorg/types::Role | | my-lib | Config | my-lib::Config | | (current project) | LocalType | <pkgName>::LocalType |

The pkgPrefix is auto-detected from your package.json name field, or can be set manually.

Registry API

The runtime registry (__reflectRegistry) is available globally:

// These are internal — you normally use type_of<T>() and isOfType<T>() instead

__reflectRegistry.register(fqn, typeInfo, guard?)  // Register a type
__reflectRegistry.getTypeInfo(fqn)                  // Get TypeInfo by FQN
__reflectRegistry.isOfType(fqn, obj)                // Check object against registered guard
__reflectRegistry.has(fqn)                          // Check if type is registered
__reflectRegistry.list()                            // List all registered FQNs

API Summary

| API | Returns | Description | |-----|---------|-------------| | type_of<T>() | TypeInfo | Get runtime type metadata | | isOfType<T>(obj) | boolean | Runtime type guard (interfaces, classes, enums, unions, intersections) | | isInterface<T>(obj) | boolean | Alias for isOfType<T>() — for interface types | | obj.get_typeinfo() | TypeInfo | Only on IObject class instances |

TypeInfo API

interface TypeInfo {
  name: string
  fqn: string                    // Fully qualified name: "pkg::Type"
  kind: 'class' | 'interface' | 'enum' | 'primitive' | 'array' | 'union' | 'intersection' | 'object' | 'function' | 'literal' | 'typeAlias' | 'unknown'
  fullName: string
  properties: PropertyInfo[]
  methods: MethodInfo[]
  interfaces: string[]
  baseType: string | null
  genericArgs: TypeInfo[]
  isInterface: boolean
  isOptional: boolean
  metadata: Record<string, unknown>
}

interface PropertyInfo {
  name: string
  type: TypeInfo
  optional: boolean
  readonly: boolean
  hasGetter: boolean
  hasSetter: boolean
  metadata: Record<string, unknown>
}

interface MethodInfo {
  name: string
  returnType: TypeInfo
  parameters: ParameterInfo[]
  metadata: Record<string, unknown>
}

interface ParameterInfo {
  name: string
  type: TypeInfo
  optional: boolean
  hasDefault: boolean
}

Supported Types

| TypeScript Type | TypeInfo.kind | isOfType check | |---|---|---| | interface IX { ... } | 'interface' | Property-by-property check | | class X { ... } | 'class' | instanceof + constructor name + property check | | enum X { ... } | 'enum' | Value in enum values list | | type X = ... | 'typeAlias' | Resolves to target type | | string, number, boolean | 'primitive' | typeof check | | T[] / Array<T> | 'array' | Array.isArray + element check | | [A, B, C] (tuple) | 'array' | Length + element checks | | A \| B (union) | 'union' | Any member matches | | A & B (intersection) | 'intersection' | All members match | | 'literal' / 123 / true | 'literal' | Strict equality | | Record<K, V> | 'object' | Values check | | Partial<T>, Required<T>, Readonly<T> | 'object' | Mapped property modifiers | | Pick<T, K>, Omit<T, K> | 'object' | Filtered properties | | Function | 'function' | typeof === 'function' | | any, unknown | 'unknown' | Always true | | Generic IX<T> | 'interface' | Factory function |

IObject

IObject is a marker interface. When a class implements IObject, the plugin auto-generates a get_typeinfo() method.

class User implements IObject { ... }    // ✅ has get_typeinfo()
class PlainUser { ... }                   // ❌ no get_typeinfo()

isOfType vs isInterface

isInterface<T>(obj) is an alias for isOfType<T>(obj). Both resolve to the same registry-based guard. Use whichever reads better in context:

  • isInterface<IUser>(data) — when checking against an interface
  • isOfType<User>(data) — when checking against a class, enum, union, or any type

Options

reflect({
  // File patterns
  include: [],                    // File patterns to include
  exclude: ['node_modules'],      // File patterns to exclude

  // Package prefix for FQN (auto-detected from package.json if not set)
  pkgPrefix: '@myorg/types',      // → FQN: "@myorg/types::IUser"

  // Auto-discover reflect-usage packages (default: true)
  autoDiscover: true,

  // Control which complex types are expanded
  resolveOpts: {
    expandTypes: true,            // Master switch for complex type expansion
    expandTypeAliases: true,      // Expand type aliases
    expandIntersection: true,     // Expand intersection types
    expandUnion: true,            // Expand union types
  },
})

resolveOpts — Controlling Output Size

If you want minimal generated code, disable complex type expansion:

reflect({
  resolveOpts: {
    expandTypes: false,      // Skip all complex type resolution
    // Or individually:
    expandUnion: false,      // Union types → 'unknown'
    expandIntersection: false, // Intersection types → 'unknown'
    expandTypeAliases: false,  // Type aliases → not generated
  },
})

On-Demand Generation

The plugin only generates TypeInfo and guards for types that are actually used — i.e., types referenced in type_of<T>(), isOfType<T>(), isInterface<T>(), or classes implementing IObject. Unused types produce zero output.

Relation to farm-plugin-isinterface

farm-plugin-isinterface is the standalone version with only isInterface<T>(). farm-plugin-reflect is the full version that includes isInterface<T>() + isOfType<T>() + type_of<T>() + IObject + cross-package registry. Don't use both together — farm-plugin-reflect is a superset.

License

MIT