https://github.com/goloveychuk/tsruntime
typescript: emitting types info for using in runtime (reflection)
https://github.com/goloveychuk/tsruntime
reflect runtime typescript
Last synced: 10 months ago
JSON representation
typescript: emitting types info for using in runtime (reflection)
- Host: GitHub
- URL: https://github.com/goloveychuk/tsruntime
- Owner: goloveychuk
- License: mit
- Created: 2017-04-30T11:15:58.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2024-01-02T15:16:02.000Z (over 2 years ago)
- Last Synced: 2025-08-30T15:28:20.550Z (10 months ago)
- Topics: reflect, runtime, typescript
- Language: TypeScript
- Homepage:
- Size: 276 KB
- Stars: 44
- Watchers: 2
- Forks: 5
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# tsruntime
[](https://travis-ci.org/goloveychuk/tsruntime)
[](https://www.npmjs.com/package/tsruntime)
Typescript custom transformer for emitting types for using in runtime (reflection).
## Motivation
Sometimes you need types metadata in the runtime, e.g. to validate backend response or set `propTypes` from `interface Props{}`
## Prerequisites
- `typescript: =>5.0.0`
## Setup
- install `tsruntime` and `ts-patch`
- change `tsconfig.json`:
```js
{
"compilerOptions": {
"experimentalDecorators": true, // if you'll use decorators
"plugins": [
{ "transform": "tsruntime/dist/transform/transformer.js", "type": "program" },
],
}
}
```
- run with `ts-patch` compiler:
- `ts-node --compiler=ts-patch/compiler src/index.ts`
- `tspc`
- change `compiler` in `awesome-typescript-loader` config
- etc
### See [Example](./packages/example)
### Warning: You cannot use `transpileOnly` compiling mode and `isolatedModules` (if you want reflect types from imported modules).
## Usage:
### Using `reflect` function
```ts
import {reflect} from 'tsruntime';
interface StatsModel {
a?: number
b: string
c: Array
d: number | string | null
}
const type = reflect()
console.log(type)
const type2 = reflect()
```
On compiled code you'll have
```js
var type = tsruntime_1.reflect({
kind: 15 /*Object*/,
name: "StatsModel",
properties: {
a: {
kind: 17 /*Union*/,
types: [{ kind: 12 /*Undefined*/ }, { kind: 3 /*Number*/ }]
},
b: { kind: 2 /*String*/ },
c: {
kind: 18 /*Reference*/,
type: Array,
arguments: [{ kind: 2 /*String*/ }]
},
d: {
kind: 17 /*Union*/,
types: [
{ kind: 13 /*Null*/ },
{ kind: 2 /*String*/ },
{ kind: 3 /*Number*/ }
]
}
}
})();
```
### Using class decorators
```ts
import {Reflective, getClassType} from 'tsruntime';
@Reflective
export class StatsModel {
a?: number
b!: string
c!: Array
d!: number | string | null
}
@Reflective
class Foo extends Array {
}
console.log(getClassType(StatsModel))
console.log(getClassType(Foo))
```
On runtime you'll have
```js
// ...
StatsModel = __decorate(
[
tsruntime_1.Reflective({
kind: 19 /*Class*/,
name: "StatsModel",
properties: {
a: {
kind: 17 /*Union*/,
types: [{ kind: 12 /*Undefined*/ }, { kind: 3 /*Number*/ }]
},
b: { kind: 2 /*String*/ },
c: {
kind: 18 /*Reference*/,
type: Array,
arguments: [{ kind: 2 /*String*/ }]
},
d: {
kind: 17 /*Union*/,
types: [
{ kind: 13 /*Null*/ },
{ kind: 2 /*String*/ },
{ kind: 3 /*Number*/ }
]
}
}
})
],
StatsModel
);
// ...
```
### Using types info
```ts
import {Types, reflect} from 'tsruntime';
const isString = reflect().kind === Types.TypeKind.String
```
[Full runtime info available](./packages/tsruntime/src/runtime/publicTypes.ts)
## Customization
You can customize both `reflect` and `Reflective` to do whatever you want
```ts
import {createReflective, Types} from 'tsruntime';
const storage = {} as any
function MyReflective (key: string) {
return createReflective(reflectedType => {
return (target: any) => {
storage[key] = reflectedType
}
})
}
// typeof MyReflective('realcls') is MarkReflective type.
@MyReflective('realcls')
class Cls {
prop = 42
}
// in compiled code - @MyReflective('realcls')({kind: ...})
const clsType = storage['realcls']
const validateResp = createReflective(
reflectedType => (resp: unknown) => { //should have
if (reflectedType.kind === Types.TypeKind.String) {
return typeof resp === 'string'
}
}
)
const isValid = validateResp('asd')
// in compiled code - validateResp({kind: ...})('asd')
```
### How it works
`createReflective` expects `reflectedType => T` to be passed as arg.
It returns `T` and marks it as reflectable (`type MarkReflective`).
Transformer see that symbol have type `MarkReflective` and calls it with `reflectedType`.
For function it gets type from first generic argument (thats why you need ``), for decorator - from class declaration, related to decorator.