farm-plugin-isinterface
v0.2.0
Published
Runtime interface guards for FarmFE — generates isInterface<T>(obj) type guards from TypeScript interfaces at compile time
Maintainers
Readme
farm-plugin-isinterface
Runtime interface type guards for FarmFE — generates isInterface<T>(obj) guard functions from TypeScript interfaces at compile time.
Why?
TypeScript interfaces are erased at runtime. You can't check if an object conforms to an interface at runtime — until now. This plugin reads your TypeScript interfaces and generates efficient runtime type guard functions, then transforms isInterface<T>(obj) calls into direct guard invocations.
Install
pnpm add -D farm-plugin-isinterfaceUsage
1. Define interfaces
// user.ts
export interface IUser {
name: string
age: number
email?: string
roles: string[]
}
export interface IAdmin extends IUser {
permissions: string[]
}2. Use isInterface<T>(obj) in your code
// main.ts
import type { IUser, IAdmin } from './user'
const data: unknown = JSON.parse(input)
if (isInterface<IUser>(data)) {
console.log(data.name) // TypeScript knows data is IUser
console.log(data.age) // ✅ type-safe access
}
if (isInterface<IAdmin>(data)) {
console.log(data.permissions) // ✅ type-safe access
}3. Configure FarmFE
// farm.config.ts
import { defineConfig } from '@farmfe/core'
import isInterface from 'farm-plugin-isinterface'
export default defineConfig({
plugins: [isInterface()],
})How It Works
At compile time, the plugin:
- Parses all
interfacedeclarations in your TypeScript files - Generates runtime guard functions like
__isInterface_IUser(obj) { ... } - Transforms
isInterface<IUser>(obj)→__isInterface_IUser(obj) - Prepends the guard functions to the module
Generated Guard Example
For IUser above, the plugin generates:
function __isInterface_IUser(obj) {
if (obj === null || typeof obj !== 'object') return false;
if (!(typeof obj.name === 'string')) return false;
if (!(typeof obj.age === 'number')) return false;
if (obj.email !== undefined && !(typeof obj.email === 'string')) return false;
if (!(Array.isArray(obj.roles) && obj.roles.every(item => typeof item === 'string'))) return false;
return true;
}For IAdmin extends IUser:
function __isInterface_IAdmin(obj) {
if (obj === null || typeof obj !== 'object') return false;
if (!(Array.isArray(obj.permissions) && obj.permissions.every(item => typeof item === 'string'))) return false;
if (typeof __isInterface_IUser === 'function' && !__isInterface_IUser(obj)) return false;
return true;
}Supported Types
| TypeScript Type | Runtime Check |
|---|---|
| string, number, boolean, bigint, symbol | typeof obj === 'type' |
| null, undefined | obj === null / obj === undefined |
| T[] / Array<T> | Array.isArray(obj) && obj.every(...) |
| [A, B, C] (tuple) | Array.isArray(obj) && obj.length >= N && ... |
| A \| B (union) | (checkA \|\| checkB) |
| 'literal' / 123 / true | obj === value |
| Record<K, V> | Object.values(obj).every(...) |
| Function | typeof obj === 'function' |
| any, unknown | true (no check) |
| IOther (reference) | __isInterface_IOther(obj) if guard exists |
| { ... } (inline object) | Nested property checks |
Options
isInterface({
// File patterns to include (default: all .ts/.tsx)
include: [],
// File patterns to exclude (default: ['node_modules'])
exclude: ['node_modules'],
// Auto-generate guards for all exported interfaces (default: true)
autoGuard: true,
// Add __isInterface_* exports to the module (default: false)
exportGuards: false,
})Cross-Module References
If isInterface<IAdmin>(obj) is called in a file that doesn't define IAdmin, the plugin transforms the call to __isInterface_IAdmin(obj). Make sure the guard function is available at runtime — either:
- Define
IAdminin the same file, or - Import from a file that defines it (the guard is generated there), or
- Set
exportGuards: trueand import the guard explicitly
License
MIT
