Giter Site home page Giter Site logo

hookyns / tst-reflect Goto Github PK

View Code? Open in Web Editor NEW
321.0 8.0 11.0 1.12 MB

Advanced TypeScript runtime reflection system

License: MIT License

TypeScript 89.33% JavaScript 10.67%
generic typescript runtime-reflection typescript-transformer transformer-plugin nodejs reflection dependency-injection transformer plugin

tst-reflect's Introduction

JavaScript(/TypeScript) Runtime Types & Reflection (tst-reflect)

The mono repository of TypeScript runtime reflection packages.

tst-reflect tst-reflect-transformer License MIT Code coverage Twitter All Contributors

Readme Card

Examples | Synopsis | How to start | How does it work | Configuration [wiki] | Changelog | Contributors

Notice!

A new version of the entire system is in development!

The new version is made up of a separate tool called typegen and a new better runtime. It is possible to generate usable metadata even without the need to use any transformer. This metadata can be used manually to lookup types and modules in the project, import the modules, create instances of classes etc. This in not limited to any build system so it is usable with tsc, Webpack, Vite, esbuild, SWC, Turbopack, Rollup, Parcel etc.

However, there will also be small plugin for each build tool that allow using getType() and the generic type parameters, even for SWC and esbuild. Depending on the build system, the functionality of getType will vary. Build systems without type checking will not be able to get majority of inferred types eg. getType<typeof variable>().

Sign for participation in the new version issue #78.

About

Yeap! How the title says, this project is about runtime reflection, with working generic type parameters <TSomething>, achieved using custom TypeScript transformer plugin (package tst-reflect-transformer) and runtime stuff (package tst-reflect).

Features

  • clear Reflection system, generating JS metadata library - can be de facto standard for TS reflection,
  • you can use regular TypeScript,
  • no decorators nor any other kind of hints needed,
  • no problem with the types from 3rd party packages,
  • no pre-implemented features like validators or type-guards, just clear type information. All these features can be built on top of this package in runtime,
  • you can get the runtime type of the generic type, really!
  • you can access the type of the class in your own decorators,
  • you can get the type of a classes, interfaces, type literals, unions, intersections just all of that getType<SomeType>(),
  • you can get the type of runtime value stored in variable getType(myVar),
  • object oriented usage - you will get instance of the Type class, which contains everything,
  • inspired by the C# reflection - usage is very similar,
  • overloads of a constructors and methods supported,
  • static API to lookup types,
  • access to constructors of all the types - you can instantiate every type,
  • configuration with multiple options,
  • browser usage,
  • you can check if one type is assignable to another without instances of those type - eg. check if some class is assignable to some interface,
  • and there are probably more... and a lot of things are on TODO list.

How to Get the Type

Use function getType<TType>(): Type imported from module tst-reflect.

import { getType } from "tst-reflect";

interface IFoo {}
class Foo implements IFoo {}

getType<IFoo>();
getType<Foo>();
getType(Foo);

const foo = new Foo();
getType<typeof foo>();
getType(foo);

Base Usage

import { getType, Type } from "tst-reflect";

interface IAnimal
{
    name: string;
}

class Animal implements IAnimal
{
    constructor(public name: string)
    {
    }
}

const typeOfIAnimal: Type = getType<IAnimal>();
const typeOfAnimal: Type = getType<Animal>();

console.log(typeOfAnimal.isAssignableTo(typeOfIAnimal)); // true

Get Type of Generic Type Parameter (runtime)

import { getType } from "tst-reflect";

function printTypeProperties<TType>() 
{
    const type = getType<TType>(); // <<== get type of type parameter TType
    
    console.log(type.getProperties().map(prop => prop.name + ": " + prop.type.name).join("\n"));
}

interface SomeType {
    foo: string;
    bar: number;
    baz: Date;
}

printTypeProperties<SomeType>();

Output:

foo: string
bar: number
baz: Date

Decorator With Reflected Type Parameter

tst-reflect-transformer is able to process class decorators marked by @reflect JSDoc tag. You will be able to get Type of each decorated class.

/**
 * @reflect
 */
export function inject<TType>()
{
    const typeofClass = getType<TType>();

    return function <TType extends { new(...args: any[]): {} }>(Constructor: TType) {
        return class extends Constructor
        {
            constructor(...args: any[])
            {
                super(...type.getConstructors()[0].parameters.map(param => serviceProvider.getService(param.type)));
            }
        }
    };
}

@inject()
class A {}

@inject()
class B {}

How to Start

Usage With Plain TypeScript

  1. Install packages,
npm i tst-reflect && npm i tst-reflect-transformer -D
  1. add transformer to tsconfig.json,
{
    "compilerOptions": {
        // your options...

        // ADD THIS!
        "plugins": [
            {
                "transform": "tst-reflect-transformer"
            }
        ]
    }
}
  1. npm i ttypescript -D

In order to use transformer plugin you need TypeScript compiler which supports plugins eg. package ttypescript or you can use TypeScript compiler API manually.

  1. Now just transpile your code by ttsc instead of tsc
npx ttsc

Usage With Webpack

If you use Angular or something else which has webpack encapsulated and under its own control, this Usage variant may not work properly. Angular has own Usage description.

With ts-loader

! ts-loader is recommended because you don't need ttypescript and it has better performance than awesome-typescript-loader.

StackBlitz demo with configured project here.

  1. Install packages,
npm i tst-reflect && npm i tst-reflect-transformer -D
  1. modify your webpack config,
const tstReflectTransform = require("tst-reflect-transformer").default;

module.exports = {
    module: {
        rules: [
            {
                test: /\.(ts|tsx)$/,
                loader: "ts-loader",
                options: {
                    // ADD THIS OPTION!
                    getCustomTransformers: (program) => ({
                        before: [
                            tstReflectTransform(program, {})
                        ]
                    })
                }
            }
            // ... other rules
        ]
    }
    // ... other options
};
  1. webpack or webpack serve

With awesome-typescript-loader (deprecated)

  1. Install packages,
npm i tst-reflect && npm i tst-reflect-transformer -D
  1. add transformer to tsconfig.json,
{
    "compilerOptions": {
        // your options...

        // ADD THIS!
        "plugins": [
            {
                "transform": "tst-reflect-transformer"
            }
        ]
    }
}
  1. npm i ttypescript -D

In order to use transformer plugin you need TypeScript compiler which supports plugins eg. package ttypescript or you can use TypeScript compiler API manually.

  1. modify your webpack config,
({
    test: /\.(ts|tsx)$/,
    loader: "awesome-typescript-loader",
    options: {
        compiler: "ttypescript"
    }
})
  1. webpack or webpack serve

Using Parcel

Install Parcel plugin.

npm i parcel-plugin-ttypescript

Using Rollup

Install Rollup plugin

npm i rollup-plugin-typescript2

and modify your rollup config.

import ttypescript from "ttypescript";
import tsPlugin from "rollup-plugin-typescript2";

export default {
    // your options...
    
    plugins: [
        // ADD THIS!
        tsPlugin({
            typescript: ttypescript
        })
    ]
}

Using ts-node

Modify your tsconfig.json.

{
    "compilerOptions": {
        // your options...

        "plugins": [
            {
                "transform": "tst-reflect-transformer"
            }
        ]
    },
    
    // ADD THIS!
    "ts-node": {
        // This can be omitted when using ts-patch
        "compiler": "ttypescript"
    },
}

ts-node can be a little bugged if you use reflection.metadata.type = "typelib" option!

Examples

  • Listing properties and methods of classes and interfaces StackBlitz
  • Validace structure of object by interface or class StackBlitz
  • Methods and constructors overloads StackBlitz
  • Create object with default values by interface StackBlitz

Deprecated

  • Simple Dependency Injection Run on repl.it
  • Object validation by Interface Run on repl.it

Feel free to add Your interesting examples. Just add a link to this README and make a PR.

How Does it Work

Transformer looks for all calls of getType<T>() and replace those calls by Type retrieving logic. It generates object literals describing referred types and instances of Type are created from those objects.

Metadata

Mentioned object literals describing types are called metadata. Default behavior collect metadata of all used types and generate file metadata.lib.js in project root ( location of tsconfig.json).

Metadata library file looks like this:

var {getType} = require("tst-reflect");
getType({
    k: 5,
    props: [{n: "foo", t: getType({n: "string", k: 2})}, {
        n: "bar",
        t: getType({k: 3, types: [getType({k: 6, v: "a"}), getType({k: 6, v: "b"})], union: true, inter: false})
    }]
}, 22974);
getType({k: 5, props: [{n: "foo", t: getType({n: "string", k: 2})}, {n: "bar", t: getType({n: "string", k: 2})}]}, 22969);
getType({
    n: "SomeType",
    fn: "..\\logger.ts:SomeType",
    props: [{n: "array", t: getType({k: 4, n: "Array", args: [getType(22969)]})}],
    ctors: [{params: []}],
    k: 1,
    ctor: () => SomeType
}, 22965);
getType({
    n: "Foo",
    fn: "..\\logger.ts:Foo",
    props: [{n: "prop", t: getType({n: "number", k: 2})}],
    ctors: [{params: [{n: "prop", t: getType({n: "number", k: 2})}]}],
    k: 1,
    ctor: () => Foo
}, 22976);

Synopsis

/**
 * Object representing TypeScript type in memory
 */
export declare class Type {
    static readonly Object: Type;
    static readonly Unknown: Type;
    static readonly Any: Type;
    static readonly Void: Type;
    static readonly String: Type;
    static readonly Number: Type;
    static readonly BigInt: Type;
    static readonly Boolean: Type;
    static readonly Date: Type;
    static readonly Null: Type;
    static readonly Undefined: Type;
    static readonly Never: Type;
    /**
     * Returns information about conditional type.
     */
    get condition(): ConditionalType | undefined;
    /**
     * Returns information about indexed access type.
     */
    get indexedAccessType(): IndexedAccessType | undefined;
    /**
     * List of underlying types in case Type is union or intersection
     */
    get types(): ReadonlyArray<Type>;
    /**
     * Get meta for the module of the defined constructor
     * This data is not set when the config mode is set to "universal"
     */
    get constructorDescription(): ConstructorImport | undefined;
    /**
     * Get definition of a generic type.
     */
    get genericTypeDefinition(): Type | undefined;
    /**
     * Base type
     * @description Base type from which this type extends from or undefined if type is Object.
     */
    get baseType(): Type | undefined;
    /**
     * Interface which this type implements
     */
    get interface(): Type | undefined;
    /**
     * Get type full-name
     * @description Contains file path base to project root
     */
    get fullName(): string;
    /**
     * Get type name
     */
    get name(): string;
    /**
     * Get kind of type
     */
    get kind(): TypeKind;
    /**
     * Underlying value in case of literal type
     */
    get literalValue(): any;
    /**
     * Generic type constrains
     */
    get genericTypeConstraint(): Type | undefined;
    /**
     * Generic type default value
     */
    get genericTypeDefault(): any;
    /**
     * Search the type store for a specific type
     *
     * Runs the provided filter callback on each type. If your filter returns true, it returns this type.
     *
     * @param {(type: Type) => boolean} filter
     * @returns {Type | undefined}
     */
    static find(filter: (type: Type) => boolean): Type | undefined;
    /**
     * Returns all Types contained in metadata.
     * This method is quite useless with reflection.metadata.type = "inline"; Use "typelib" type.
     */
    static getTypes(): Type[];
    static get store(): MetadataStore;
    /**
     * Returns true if types are equals
     * @param type
     */
    is(type: Type): boolean;
    /**
     * Returns a value indicating whether the Type is container for unified Types or not
     */
    isUnion(): boolean;
    /**
     * Returns a value indicating whether the Type is container for intersecting Types or not
     */
    isIntersection(): boolean;
    /**
     * Returns true whether current Type is a class with any constructor.
     */
    isInstantiable(): boolean;
    /**
     * Returns a value indicating whether the Type is a class or not
     */
    isClass(): boolean;
    /**
     * Returns a value indicating whether the Type is a interface or not
     */
    isInterface(): boolean;
    /**
     * Returns a value indicating whether the Type is an literal or not
     */
    isLiteral(): boolean;
    /**
     * Returns a value indicating whether the Type is an object literal or not
     */
    isObjectLiteral(): boolean;
    /**
     * Returns true if type is union or intersection of types
     */
    isUnionOrIntersection(): boolean;
    /**
     * Check if this is a native type ("string", "number", "boolean", "Array" etc.)
     */
    isNative(): boolean;
    /**
     * Check whether the type is generic.
     */
    isGenericType(): boolean;
    /**
     * Check if this is a primitive type ("string", "number", "boolean" etc.)
     */
    isPrimitive(): boolean;
    /**
     * Check if this type is a string
     */
    isString(): boolean;
    /**
     * Check if this type is a number
     */
    isNumber(): boolean;
    /**
     * Check if this type is a symbol
     */
    isSymbol(): boolean;
    /**
     * Check if this type is a boolean
     */
    isBoolean(): boolean;
    /**
     * Check if this type is an array
     */
    isArray(): boolean;
    /**
     * Check if this type is a promise
     */
    isPromise(): boolean;
    /**
     * Check if this type is a Tuple
     */
    isTuple(): boolean;
    /**
     * Check if this type is an any
     */
    isAny(): boolean;
    /**
     * Check if this type is a "unknown".
     */
    isUnknown(): boolean;
    /**
     * Check if this type is a "undefined" literal.
     */
    isUndefined(): boolean;
    /**
     * Check if this type is a "null" literal.
     */
    isNull(): boolean;
    /**
     * Check if this type is a "true" literal.
     */
    isTrue(): boolean;
    /**
     * Check if this type is a "false" literal.
     */
    isFalse(): boolean;
    /**
     *
     * @return {boolean}
     */
    isObjectLike(): boolean;
    /**
     * Determines whether the object represented by the current Type is an Enum.
     * @return {boolean}
     */
    isEnum(): boolean;
    /**
     * Returns information about the enumerable elements.
     */
    getEnum(): EnumInfo | undefined;
    /**
     * Constructor function in case Type is class
     */
    getCtor(): Promise<{
        new (...args: any[]): any;
    } | undefined>;
    /**
     * Returns array of function call signatures.
     */
    getSignatures(): ReadonlyArray<FunctionInfo>;
    /**
     * Returns array of type parameters.
     */
    getTypeParameters(): ReadonlyArray<Type>;
    /**
     * Returns type arguments in case of generic type
     */
    getTypeArguments(): ReadonlyArray<Type>;
    /**
     * Returns constructor description when Type is a class
     */
    getConstructors(): ReadonlyArray<ConstructorInfo> | undefined;
    /**
     * Returns array of properties
     */
    getProperties(): ReadonlyArray<PropertyInfo>;
    /**
     * Returns array of indexes
     */
    getIndexes(): ReadonlyArray<IndexInfo>;
    /**
     * Returns array of methods
     */
    getMethods(): ReadonlyArray<MethodInfo>;
    /**
     * Returns array of decorators
     */
    getDecorators(): ReadonlyArray<Decorator>;
    /**
     * Returns object with all methods and properties from current Type and all methods and properties inherited from base types and interfaces to this Type.
     * @return {{properties: {[p: string]: PropertyInfo}, methods: {[p: string]: MethodInfo}}}
     */
    flattenInheritedMembers(): {
        properties: {
            [propertyName: string]: PropertyInfo;
        };
        methods: {
            [methodName: string]: MethodInfo;
        };
    };
    /**
     * Determines whether the class represented by the current Type derives from the class represented by the specified Type
     * @param {Type} classType
     */
    isSubclassOf(classType: Type): boolean;
    /**
     * Determines whether the current Type derives from the specified Type
     * @param {Type} targetType
     */
    isDerivedFrom(targetType: Type): boolean;
    /**
     * Determines whether the Object represented by the current Type is structurally compatible and assignable to the Object represented by the specified Type
     * @param {Type} target
     * @return {boolean}
     * @private
     */
    isStructurallyAssignableTo(target: Type): boolean;
    /**
     * Determines whether an instance of the current Type can be assigned to an instance of the specified Type.
     * @description This is fulfilled by derived types or compatible types.
     * @param target
     */
    isAssignableTo(target: Type): boolean;
    /**
     * Returns string representation of the type.
     */
    toString(): string;
}

/**
 * Kind of type
 */
export declare enum TypeKind
{
    /**
     * Interface
     */
    Interface = 0,
    /**
     * Class
     */
    Class = 1,
    /**
     * Native JavaScript/TypeScript type
     */
    Native = 2,
    /**
     * Container for other types in case of types union or intersection
     */
    Container = 3,
    /**
     * Type reference created during type checking
     * @description Usually Array<...>, ReadOnly<...> etc.
     */
    TransientTypeReference = 4,
    /**
     * Some specific object
     * @description Eg. "{ foo: string, bar: boolean }"
     */
    Object = 5,
    /**
     * Some subtype of string, number, boolean
     * @example <caption>type Foo = "hello world" | "hello"</caption>
     * String "hello world" is literal type and it is subtype of string.
     *
     * <caption>type TheOnlyTrue = true;</caption>
     * Same as true is literal type and it is subtype of boolean.
     */
    LiteralType = 6,
    /**
     * Fixed lenght arrays literals
     * @example <caption>type Coords = [x: number, y: number, z: number];</caption>
     */
    Tuple = 7,
    /**
     * Generic parameter type
     * @description Represent generic type parameter of generic types. Eg. it is TType of class Animal<TType> {}.
     */
    TypeParameter = 8,
    /**
     * Conditional type
     */
    ConditionalType = 9,
    /**
     * Indexed access type
     * @description Eg. get<K extends keyof TypeKind>(key: K): ==>> TypeKind[K] <<==
     */
    IndexedAccess = 10,
    /**
     * Typescript "module"
     * @description Value module or namespace module
     */
    Module = 11,
    /**
     * Specific method used as type
     */
    Method = 12,
    /**
     * Enum
     */
    Enum = 13
}

export declare enum Accessor
{
    None = 0,
    Getter = 1,
    Setter = 2
}

export declare enum AccessModifier
{
    Private = 0,
    Protected = 1,
    Public = 2
}

export interface ConditionalType
{
    /**
     * Extends type
     */
    extends: Type;
    /**
     * True type
     */
    trueType: Type;
    /**
     * False type
     */
    falseType: Type;
}

/**
 * Property description
 */
export class Property
{
    /**
     * Property name
     */
    readonly name: string;
    /**
     * Property type
     */
    readonly type: Type;
    /**
     * Optional property
     */
    readonly optional: boolean;
    /**
     * Access modifier
     */
    readonly accessModifier: AccessModifier;
    /**
     * Accessor
     */
    readonly accessor: Accessor;
    /**
     * Readonly
     */
    readonly readonly: boolean;
    /**
     * Returns array of decorators
     */
    getDecorators(): ReadonlyArray<Decorator>;
}

/**
 * Decoration description
 */
export class Decorator {
    /**
     * Decorator name
     */
    name: string;
    /**
     * Decorator full name
     */
    fullName?: string;
    /**
     * List of literal arguments
     */
    getArguments(): Array<any>;
}

/**
 * Method parameter description
 */
export interface MethodParameter
{
    /**
     * Parameter name
     */
    name: string;
    /**
     * Parameter type
     */
    type: Type;
    /**
     * Parameter is optional
     */
    optional: boolean;
}

export declare class MethodBase
{
    /**
     * Parameters of this method
     */
    getParameters(): ReadonlyArray<MethodParameter>;
}

/**
 * Method details
 */
export declare class Method extends MethodBase
{
    /**
     * Name of this method
     */
    get name(): string;

    /**
     * Return type of this method
     */
    get returnType(): Type;

    /**
     * Method is optional
     */
    get optional(): boolean;

    /**
     * Access modifier
     */
    get accessModifier(): AccessModifier;

    /**
     * Returns list of generic type parameter.
     * @return {Array<Type>}
     */
    getTypeParameters(): ReadonlyArray<Type>;

    /**
     * Returns array of decorators
     */
    getDecorators(): ReadonlyArray<Decorator>;
}

/**
 * Constructor details
 */
export declare class Constructor extends MethodBase
{
}

export interface EnumInfo {
    /**
     * Get enum enumerators/items (keys).
     */
    getEnumerators(): string[];
    /**
     * Get values.
     */
    getValues(): any[];
    /**
     * Get enum entries (key:value pairs).
     */
    getEntries(): Array<readonly [enumeratorName: string, value: any]>;
}

Contributors ✨

Thanks go to these wonderful people (emoji key):


Roman Jámbor

💻 🚧 📖 👀 💡 🤔 🚇 💬 ⚠️

Sam Parton

💻 🐛 🤔

Filmos

🐛

Kévin Dunglas

🤔

usaccounts

🐛

caiodallecio

🤔

Daniel Shmuglin

🐛 🤔

Avin

🐛 💻

Joe Ferner

💻

David Katz

🐛

Jamesb | Experimental Learning

🐛

Carlos Zimmerle

🤔 🐛

This project follows the all-contributors specification. Contributions of any kind are welcome!

Motivation

I'm developing this Reflection system for own Dependency Injection system, to allow registering and resolving based on types. Something like:

serviceCollection.addTransient<ILog, Log>();
...
serviceProvider.getService<ILog>();

Where getService() takes care about constructor's parameters, based on their types, and resolve everything.

License

This project is licensed under the MIT license.

tst-reflect's People

Contributors

allcontributors[bot] avatar avin-kavish avatar hookyns avatar idevelopthings avatar joeferner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tst-reflect's Issues

"Error: Unknown type kind" when class has an array property

Reproducible example:

import {getType, Type} from "tst-reflect";

class test {
    public list: string[];
}
console.log(getType<test>())

Generated error:

Error: Unknown type kind
    at Object.getTypeKind (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/helpers.js:34:11)
    at getTypeProperties (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:46:28)
    at Object.getTypeCall [as default] (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:66:27)
    at members.filter.map (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getProperties.js:14:37)
    at Array.map (<anonymous>)
    at Object.getProperties (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getProperties.js:12:14)
    at getTypeProperties (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:50:32)
    at Object.getTypeCall [as default] (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:66:27)
    at visit (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/visitation.js:29:67)
    at visitor (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/visitation.js:41:30)
Error: Unknown type kind
    at Object.getTypeKind (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/helpers.js:34:11)
    at getTypeProperties (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:46:28)
    at Object.getTypeCall [as default] (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:66:27)
    at members.filter.map (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getProperties.js:14:37)
    at Array.map (<anonymous>)
    at Object.getProperties (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getProperties.js:12:14)
    at getTypeProperties (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:50:32)
    at Object.getTypeCall [as default] (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/getTypeCall.js:66:27)
    at visit (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/visitation.js:29:67)
    at visitor (/home/runner/tst-reflect-example-01/src/node_modules/tst-reflect-transformer/src/visitation.js:41:30)
Unknown type kind

If you replace string[] with string the error is gone and everything works fine, so it has something to do with arrays in the class.

Optional boolean resolves to true, false or undefiend

class Foo {
  boolField? = false
}

This resolves to a container type with undefined | true | false so type.isBoolean() check on either the true type or the false type fails.

[
  TypeActivator {
    _name: 'false',
    _fullName: 'false',
    _kind: 2,
    _constructors: [],
    _properties: [],
    _indexes: [],
    _methods: [],
    _decorators: [],
    _typeParameters: [],
    _ctor: undefined,
    _ctorDesc: ConstructorImportActivator {},
    _interface: undefined,
    _isUnion: false,
    _isIntersection: false,
    _types: [],
    _literalValue: undefined,
    _typeArgs: [],
    _conditionalType: undefined,
    _indexedAccessType: undefined,
    _functionType: undefined,
    _genericTypeConstraint: undefined,
    _genericTypeDefault: undefined,
    _isGenericType: false,
    _genericTypeDefinition: undefined,
    _baseType: LazyType {
      typeResolver: [Function (anonymous)],
      resolvedType: [TypeActivator]
    }
  },
  TypeActivator {
    _name: 'true',
    _fullName: 'true',
    _kind: 2,
    _constructors: [],
    _properties: [],
    _indexes: [],
    _methods: [],
    _decorators: [],
    _typeParameters: [],
    _ctor: undefined,
    _ctorDesc: ConstructorImportActivator {},
    _interface: undefined,
    _isUnion: false,
    _isIntersection: false,
    _types: [],
    _literalValue: undefined,
    _typeArgs: [],
    _conditionalType: undefined,
    _indexedAccessType: undefined,
    _functionType: undefined,
    _genericTypeConstraint: undefined,
    _genericTypeDefault: undefined,
    _isGenericType: false,
    _genericTypeDefinition: undefined,
    _baseType: LazyType {
      typeResolver: [Function (anonymous)],
      resolvedType: [TypeActivator]
    }
  }
]

Get type args for things like Promise<T>

Impressed by this project, I loved the reflection and meta programming possibilities in C# and love that you are adding it into TS!

I am wondering if it would be possible to get full type info for things like Promise<T>?

Eg. currently for getType<Promise<string>>() I get:

{
  "_name": "PromiseConstructor",
  "_fullName": "PromiseConstructor",
  "_kind": 4,
  "_constructors": [],
  "_properties": [],
  "_methods": [],
  "_decorators": [],
  "_typeParameters": [],
  "_ctorDesc": {},
  "_isUnion": false,
  "_isIntersection": false,
  "_types": [],
  "_typeArgs": [],
  "_baseType": {
    "_name": "Object",
    "_fullName": "Object",
    "_kind": 2,
    "_constructors": [],
    "_properties": [],
    "_methods": [],
    "_decorators": [],
    "_typeParameters": [],
    "_ctorDesc": {},
    "_isUnion": false,
    "_isIntersection": false,
    "_types": [],
    "_typeArgs": []
  }
}

Would be happy to help however I can in implementing this, if you are able to guide me a bit to the correct areas of the codebase :)

Runtime generic class type parameters

Support class generic parameters same ways the method generic parameters.

class Foo<TGenericClassType>
{
    doSomething() {
        getType<TGenericClassType>();
    }
}

Method and Function overloads

Hello folks!

I was wondering if there's an easy way to detect/list functions'/methods' overloads? At the README is listed "overloads of a constructors and methods supported," but I couldn't find a real example or snippet to guide me on that matter (though that shouldn't be hard to do it manually, that's why I bolded "an easy way" at the beginning).

Thanks in advance and great library!

Reflect on functions

I have a use case to reflect and get the parameter information of functions. Currently, it looks like it's not possible to do. I tried

  function baz(a: string) {}

  const runtimeType = getType<typeof baz>()
  console.log(runtimeType)

and got the error,

/node_modules/tst-reflect-transformer/dist/helpers.js:81
    throw new Error("Unknown type kind");
          ^
at getTypeKind (/node_modules/tst-reflect-transformer/dist/helpers.js:81:11)
at getTypeDescription (/node_modules/tst-reflect-transformer/dist/getTypeDescription.js:227:44)
at getTypeCall (/node_modules/tst-reflect-transformer/dist/getTypeCall.js:21:71)
at processGetTypeCallExpression (/node_modules/tst-reflect-transformer/dist/processGetTypeCallExpression.js:21:46)
at mainVisitor (/node_modules/tst-reflect-transformer/dist/visitors/mainVisitor.js:26:93)
at Context._visitor (/node_modules/tst-reflect-transformer/dist/contexts/Context.js:9:35)
at visitNode (/node_modules/typescript/lib/typescript.js:89421:23)
at Object.visitEachChild (/node_modules/typescript/lib/typescript.js:89933:236)
at mainVisitor (/node_modules/tst-reflect-transformer/dist/visitors/mainVisitor.js:85:15)
at Context._visitor (/node_modules/tst-reflect-transformer/dist/contexts/Context.js:9:35)

Plugin typescript: @rollup/plugin-typescript TS5023: Unknown compiler option 'reflection'.

I tried to configure Rollup and found an error!

"rollup": "^2.70.1",

tsconfig.json

{
	"compilerOptions": {
		"moduleResolution": "Node",
		"module": "ES2020",
		"target": "ES2020",
		"strict": true,
		"sourceMap": true,
		"plugins": [
			{
				"transform": "tst-reflect-transformer"
			}
		],
		"reflection": {
			"metadata": false,
			"debugMode": false
		}
	}
}

Error:

(!) Plugin typescript: @rollup/plugin-typescript TS5023: Unknown compiler option 'reflection'.
[!] (plugin typescript) Error: @rollup/plugin-typescript: Couldn't process compiler options
Error: @rollup/plugin-typescript: Couldn't process compiler options
    at error (/Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/rollup/dist/shared/rollup.js:198:30)
    at throwPluginError (/Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/rollup/dist/shared/rollup.js:21847:12)
    at Object.error (/Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/rollup/dist/shared/rollup.js:22570:20)
    at emitParsedOptionsErrors (/Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/@rollup/plugin-typescript/dist/index.js:356:17)
    at Object.buildStart (/Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/@rollup/plugin-typescript/dist/index.js:736:13)
    at /Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/rollup/dist/shared/rollup.js:22779:37
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Promise.all (index 2)
    at /Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/rollup/dist/shared/rollup.js:23567:13
    at catchUnfinishedHookActions (/Users/khusamov/Documents/repo/github.com/khusamov/typescript-reflection/tst-reflect-rollup/node_modules/rollup/dist/shared/rollup.js:23077:20)

Different Array<T> and Promise<T> types getting assigned the same metadata store id

I ran into an issue with using getType on arrays and promises.

const x = getType<string[]>().getTypeArguments()[0]; // returns string
const y  = getType<number[]>().getTypeArguments()[0] // also returns string

I cloned the library and ran in debug mode inside the test suite:

image

I think the problem is that string[] and number[] are getting assigned the same metadata store id.

Let me know if I can help in any way, maybe this is already fixed in the update you are working on.

Question

Hey, sorry I wanted to reach out, but couldn't find any contact info.

I was working on a similar package with some other useful features, then I discovered yours, I really like what you've put together here

Is there anything you're against adding to this reflection wise and would you be open to me submitting prs with some of the features I'd love? :)

It doesn't make sense for me to release yet another package and such, it would make more sense for people with the same goal to work on a similar thing 😅

I was working on some reflection utilities but didn't get as far as you did, however I finished a system where interfaces have a type on runtime, which also have some reflection information - in a similar fashion to your "getType<Class/Interface>", it's possible to use "Interface" which the original declaration is replaced with an object and has some other things with it(still experimenting with the idea, but it allows for our interfaces to have a normal/available type on runtime, rather than vanishing from our code essentially) - I also did this for an IOC container implementation, similar to your goal here :D

If you'd like to discuss more, send me a dm on my twitter its on my profile... or we can discuss here, I don't mind

Some container types have empty string for `fullName` so equaility can't be checked with `is`

export type ManagedReference<Other> = Other & Ref<any, Other> // Ref is a class

// When used in this way, fullName is empty
ManagedReference<Car | null>

// When used in this way, fullName is ManagedReference
ManagedReference<Car>

TypeActivator {
  _name: '',
  _fullName: '',
  _kind: 3,
  _constructors: [],
  _properties: [],
  _indexes: [],
  _methods: [],
  _decorators: [],
  _typeParameters: [],
  _ctor: undefined,
  _ctorDesc: ConstructorImportActivator {},
  _interface: undefined,
  _isUnion: false,
  _isIntersection: true,
  _types: [
    LazyType {
      typeResolver: [Function (anonymous)],
      resolvedType: [TypeActivator]
    },
    LazyType {
      typeResolver: [Function (anonymous)],
      resolvedType: [TypeActivator]
    }
  ],

Support decorators from TS 4.8

Seems like I've got an issue with the transformer over a specific version. The tests run on a fork but it fails over the project using it, as the resulted node_modules/tst-reflect-transformer/dist/getDecorators.js.
if (!(declaration === null || declaration === void 0 ? void 0 : declaration.decorators)) {
return undefined;
}

This is the typescript version resolved:
typescript@^4.0.0:
version "4.8.2"

tst version is the latest fork, same for the transformer.

ttypescript@^1.5.13:
version "1.5.13"

`@deprecated
decorators has been removed from Node and merged with modifiers on the Node subtypes that support them. Use ts.canHaveDecorators() to test whether a Node can have decorators. Use ts.getDecorators() to get the decorators of a Node.

For example:

const decorators = ts.canHaveDecorators(node) ? ts.getDecorators(node) : undefined;
'decorators' is deprecated.ts(6385)
typescript.d.ts(7918, 12): The declaration was marked as deprecated here.`

:)

Package json:
Using latest fork
{ "name": "test", "version": "1.0.0", "description": "", "scripts": { "build": "ttsc ./src/index.ts" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "ttypescript": "^1.5.13", "typescript": "^4.0.0" }, "dependencies": { "tst-reflect": "file:/home/tst-reflect/runtime", "tst-reflect-transformer": "file:/home/tst-reflect/transformer" } }

code:

`
import { getType, reflect } from "tst-reflect";

function fooBar(: any, __: any) {
console.log(
, 1);
}

@reflect()
class Test {
@foobar
id: number;
}

(async () => {
console.log(
getType()
.getProperties()
.map((x) => x.getDecorators())
);
})();

`

Types are being overwrritten across files in inline mode

_ßr.Type.store.set( calls in different files are overwriting each other when they have the same id.

Suggestion: I think the singleton behavior is good. It means I can access all the types of the library using Type.store. I think they just need unique IDs.

Unable to get type arguments of class

It looks like reading the type arguments of a class doesn't work. Inner Type of type aliases resolve correctly, but not classes.

type Foo<Type> = Type & {}

class Bar<Type> {}

@inject()
export class Baz {
  foo: Foo<string>   // type.innerTypes contains string

  bar: Bar<string>   // type.innerTypes is empty. type.getTypeParameters() returns Type. type.getTypeArguments() is empty
}

Getters and setters ?

Is it possible to check if a given property has/is a getter or setter? Would you be interested in a PR ?

Type Forwarding

Is there a way to somehow hint that a type parameter should be forwarded because right now it doesn't seem to work?

/**
 * @reflect
 */
function bar<TType>(name: string, description: string) {
  const typeInfo = getType<TType>()

  console.log(typeInfo.name) // Logs 'TType' instead of class name

  return (target, key, descriptor) => {
    return descriptor
  }
}

/**
 * @reflect
 */
function foo<TType>(name: string) {
  return bar<TType>(name, 'description')
}

class Test {
  @foo('test')
  public fetch() {
  }
}

This also seems to print a warning where the decorator foo is used:

[WRN] tst-reflect-transformer: Unexpected decorator argument. Only constant values are allowed.

If I call @bar('test', 'description') directly instead it works fine and I don't get the warning


I looked at the generated JS and the issue seems to be that types aren't forwarded when expected:

/**
 * @reflect
 */
function bar(name, description) {
  var __genericParams__ = arguments[--arguments.length];
    delete arguments[arguments.length];
    const typeInfo = (__genericParams__ && __genericParams__.TType);
    // ...
}

/**
  * @reflect
  */
function foo(name) {
  var __genericParams__ = arguments[--arguments.length]; 
  delete arguments[arguments.length]; 
  return bar(name, 'description', { TType: _ßr.Type.store.get(30244) }); // <-- The issue is here
  // I would expect to see something like:
  // return bar(name, 'description', { TType: __genericParams__.TType });
}

I tried looking through examples that were in the repo but I can't really find anything. This seems a bit unintuitive right now without forwarding

imports from tst-reflect

@Hookyns having some issues with the import, probably some bug in the transformer that messes up the import?
Assuming I need both getType and TypeKind imports, the only way it works for me is

import { getType } from "tst-reflect";
import { TypeKind } from "tst-reflect";

Unifying into a single import (linters will note on that) like import { getType, TypeKind } from "tst-reflect"; fails with

tst_reflect_1 is not defined
ReferenceError: tst_reflect_1 is not defined

Tried also import * as Reflect from "tst-reflect" and then Reflect.getType<MyInterface>() - also no luck (in this case getType fails to reflect and returns an empty Type).

New config options include/exclude

Add two config options include and exclude which will be array of Glob patterns matching modules (files). Types from those modules will be automatically processed and added to metadata library.

Type is not a Type. But is a Function which returns Type when type is recursive

I've tried this sample with recursive type. And i found that type is a function, not a Type.
I have last version of tst-reflect and ts-node.

import { getType } from 'tst-reflect';

type Settings = {
  users: User[];
};

type User = {
  friends: User[];
};

console.log(
  getType<Settings>()
    .getProperties()[0]
    .type.getTypeArguments()[0]
    .getProperties()[0]
    .type.isObjectLike()) // isObjectLike() is not a function

Generate metadata about Modules

Proposed Changes

  1. Create new class Module
class Module {
    // Pointer to "native" module; created by system; assigned to all native types such as number, string, Array etc..
    static readonly Native: Module;

    // Name of the module?? File name maybe..
    readonly name: string;

    // Relative path to the module
    readonly path: string;

    // List of Types exported from the module
    getExports(): Type[];

    static find(filter: (module: Module) => boolean): Module | undefined;
    static getModules(): Module[];
}
  1. add property module into Type
class Type{
    readonly module: Module;
    // ...
}
  1. add new config option reflection.metadata.modules: boolean = false.

Notes

This can be very handy for server usage, but somebody might find this useful on client too so it should be optional. Disabled by default.

Question: Can I use this inside a property decorator?

Hi there,

first of all this seems to be a very interesting project and maybe it's ver useful.
I wonder if I could use this in property decorators to get the type during runtime. I am planing to build a Mongoose Schema definition based on the class I am writing. For this I think I need to do something like this:

// My class could look like this
export default class Test {

    @Attr() myAttr!: boolean;

}

// The decorator implementation could look like this

export function Attr(options?: OptionsSchema): PropertyDecorator {
    return (target, propertyName) {
        const type = getType<typeof target>();
        const propType = type[propertyName];
        // some magic...
    }
}

Do you think this could work? I am still in preparation of my project so I can't test it yet.
What I want to avoid is something like this:

export default class Test {

    @Attr<Test>() myAttr!: boolean;

}

export function Attr<TType>(options?: OptionsSchema): PropertyDecorator {
    return (target, propertyName) {
        const type = getType<TType>();
        const propType = type[propertyName];
        // some magic...
    }
}

const _ßr = require("tst-reflect"); // SyntaxError: Identifier '_ßr' has already been declared

I am using yarn workspaces, project structure is like this
@megarepo/backend
@megarepo/shared

backend imports from shared.

I have added the tst-reflect setup to both packages, and I'm using ts-patch.

My use case is reflecting the type names from shared when they are passed as generic arguments to calls in backend. Things seemed to be working OK, I can compile the shared project and use gettype(), however now it looks like require("tst-reflect") is being injected into all of the .js files in dist, the error below occurs when I try to run build backend:

\rootpath\packages\shared\dist\SharedClass.js:6
const _ßr = require("tst-reflect");
      ^

SyntaxError: Identifier '_ßr' has already been declared

Initially I was able to build and run both packages and reflect some type names, but I noticed generic type arguments didn't work so I omitted that code, and the next build failed. The line number referred to in the error doesn't contain _ßr = which may suggest an issue with the transformer.

There's a lot going on, so generating a repro may be a challenge, I am hoping this is a known issue with a fix, but I will do my best if required.

getCtor returns undefined when class property refers to another class that is defined after it

This doesn't work,

@entity
export class Cat {
  id = 0
  toys: Toy[] = []
}

@entity
export class Toy {
  id = 0
}

const type = getType<Toy>()
await type.getCtor()  // undefined

This works,

@entity
export class Toy {
  id = 0
}

@entity
export class Cat {
  id = 0
  toys: Toy[] = []
}

const type = getType<Toy>()
await type.getCtor()  // [class Toy]

It's the toys: Toy[] = [] line causing the issue, it works when I comment it out. It looks like it only happens for array types, Toy[]. just a direct reference works fine.

Unify all JSDoc tags to @reflect

Proposed Changes

  • Remove @reflectGeneric,
  • remove @reflectDecorator,
  • create new JSDoc tag @reflect

Note

It's easier to remember and there is no need to have separated tags.

Type.ctor in undefined when type is not exported

Bug

Current Behavior

class Foo {}

const FooCtor = getType<Foo>().ctor; // undefined

FooCtor is undefined.

Expected Behavior

Because getType<>() is used on local (even unexported) class, it should have ctor reference.

fullname, name returns empty for types

import { getType } from "tst-reflect";

function printTypeProperties<TType>() {
    const type = getType<TType>();
    console.log(type.name)
    console.log(type.fullName)
}

type SomeType = {
    foo: string;
    bar: number;
    baz: Date;
}
printTypeProperties<SomeType>();

New config option onlySafeImports

Proposed Changes

  • Add option reflection.onlySafeImports: boolean,
  • in case the onlySafeImports is true, all Type descriptions of types from node_modules will have plain Object as ctor.

Notes

Type.getTypes().forEach(type => type.ctor) is dangerous action, because if some Type is from node_modules and it looks like:

export class Foo {}
deleteAllTheFilesOnYourPc()

it can do some damage.

New include/exclude config options can exclude all the node_modules types, so there is an option to ignore them all.
But somebody would want to work with those types, but stay safe and tell: "No, don't import those modules in case I acidentaly do that."

Get type of runtime value

Add .getType() to Object prototype, which will allow get real type of value in variable, just like C# do.

Proposed Changes

  • Create const TypeSymbol = Symbol("tst-reflect-type");,
  • for reflected classes generate Class[TypeSymbol] = getType<Class>()
  • inruntime package declare
Object.defineProperty(Object.prototype, "getType", {
    enumerable: false,
    value: () => function() { return this.constructor[TypeSymbol]; }
});

Notes

Example

const foo: any = new Class();
foo.getType(); // => Type.ctor = Class

Write Tests

Write tests, at least for core features before v1.0.0.

There is no way to get Record type arguments

Hi. I've tried to get Record generic types, but nothing returned.

const t = getType<Record<string, boolean>>()
console.log(t.getTypeParameters(), t.getTypeArguments()) // [], []

nested interface properties are missing?

Hola.
Are nested interfaces supported (see code below)?

interface Bar {
  age: number;
}

interface Foo {
  name: string;
  nested: Bar;
}

const typeOfFoo: Type = getType<Foo>();

typeOfFoo.getProperties()[1].type.getProperties() // is empty - expected???

thanks!

Unable to access parameter decorators

function a(obj: Object, key: string, prop: number) {}

class Foo {
  boo?: boolean | null

  foo(@a x: string) {}
}

console.dir(
  getType<Foo>()
    .getMethods()
    .map(p => p.getParameters().map(p => p.type.getDecorators())),
)

Returns empty, I think getDecorators should be on the parameter itself and not the type?

Constructor(Type.ctor) type problems

Not sure if this is a known issue or not, was going to figure a way to fix it if so.
Also saw some TODO in the code, so I'm not sure if you have some issues in mind etc.

This is my TS:

import {SomeServiceClass} from "./tests/Classes/ControllerSetup/SomeServiceClass";
import {getType} from "tst-reflect";

const someServiceClass = getType<SomeServiceClass>();

const ctor = someServiceClass.ctor;

const t = Reflect.construct(ctor, []);

This is the generated JS:

"use strict";
Object.defineProperty(exports, "__esModule", {value : true});
const tst_reflect_1 = require("tst-reflect");
(0, tst_reflect_1.getType)({
	k     : 0,
	n     : "SomeServiceInterface",
	fn    : "@envuso/container/src/tests/Classes/ControllerSetup/SomeServiceInterface.ts:SomeServiceInterface#21493",
	props : [{n : "propertyName", t : (0, tst_reflect_1.getType)({n : "string", k : 2}), am : 2, acs : 0, ro : false, o : false}],
	meths : [{n : "randomMethod", params : [], rt : (0, tst_reflect_1.getType)({n : "any", k : 2}), tp : [], o : false, am : 2}],
}, 21493);
(0, tst_reflect_1.getType)({
	k     : 1,
	n     : "SomeServiceClass",
	fn    : "@envuso/container/src/tests/Classes/ControllerSetup/SomeServiceClass.ts:SomeServiceClass#21497",
	props : [{n : "propertyName", t : (0, tst_reflect_1.getType)({n : "string", k : 2}), am : 2, acs : 0, ro : false, o : false}],
	meths : [{
		n      : "getMessage",
		params : [],
		rt     : (0, tst_reflect_1.getType)({n : "string", k : 2}),
		tp     : [],
		o      : false,
		am     : 2,
	}, {n : "randomMethod", params : [], rt : (0, tst_reflect_1.getType)({n : "void", k : 2}), tp : [], o : false, am : 2}],
	ctors : [{params : []}],
	ctor  : () => SomeServiceClass_1.SomeServiceClass,
	iface : (0, tst_reflect_1.getType)(21493),
}, 21497);
const someServiceClass = (0, tst_reflect_1.getType)(21497);
const ctor             = someServiceClass.ctor;
const t                = Reflect.construct(ctor, []);
//# sourceMappingURL=Test.js.map

Essentially the import for "SomeServiceClass" is removed from the file

So when we call Reflect.construct(ctor, []) it cannot reference the actual module and this error is thrown:
ReferenceError: SomeServiceClass_1 is not defined

When I use the metadata file, it just becomes null instead 🤔

Metadata file and the Type::findType(): Type | undefined and Type::getTypes(): Array<Type> methods

Suggestion

Add optional behavior or change the default behavior which will generate metadata.js file which will contain all the information about types at one place.

  1. This should resolve the problem with circle dependencies,
  2. no duplicit type declarations in generated .js files,
  3. metadata can be used to find types eg. by name.

Example

const controllerType: Type = getType<Controller>();
const controllers = Type.getTypes().filter(type => type.isAssignableTo(controllerType));

Maybe something like @reflect() would be required to use on type declarations to optimize code. Or some configurations explicitly enabling metadata generation of all the types.

@reflect
export default class HomeController {}

parcel-transformer-ttypescript: Debug Failure

Hello!
Great project!

But it doesn't seem to work on Parcel.
I have prepared a repository in which I localized the error.
https://github.com/khusamov/temp-tst-reflect-on-parcel-errors

Is there any way to fix this?

Source file:

import { getType, Type } from "tst-reflect";

interface IAnimal
{
	name: string;
}

class Animal implements IAnimal
{
	constructor(public name: string)
	{
	}
}

const typeOfIAnimal: Type = getType<IAnimal>();
const typeOfAnimal: Type = getType<Animal>();

console.log(typeOfAnimal.isAssignableTo(typeOfIAnimal)); // true

Error:

parcel-transformer-ttypescript: Debug Failure.

  Error: Debug Failure.
  at /Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/typescript/lib/typescript.js:19675:89
  at String.replace (<anonymous>)
  at formatStringFromArgs
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/typescript/lib/typescript.js:19675:21)
  at Object.createCompilerDiagnostic
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/typescript/lib/typescript.js:19789:20)
  at tryReadFile
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/typescript/lib/typescript.js:40090:40)
  at Object.readConfigFile
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/typescript/lib/typescript.js:40055:32)
  at getConfigReflectionSection
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/tst-reflect-transformer/dist/config.js:11:23)
  at readConfig
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/tst-reflect-transformer/dist/config.js:27:24)
  at createConfig
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/tst-reflect-transformer/dist/config.js:51:20)
  at Activator.prepareConfig
  (/Users/khusamov/Documents/repo/github.com/khusamov/temp-tst-reflect-on-parcel-errors/node_modules/tst-reflect-transformer/dist/contexts/TransformerContext.js:51:50)

Null is usually not added to a variable's list of types

Hello.

I have tried to make some fields of class nullable, but can't get any information about allowed "nullable" behavior (same works with types, functions and interfaces too).

After a little investigation of the behavior, it turned out that the null type is added to field types in some cases. Here is an example code (ts) and "compiled" code (js):

import { NullLiteral } from "typescript";
// ...
class Test3 {
    testString1: string | null = null;
    testString2: null | string = null;
    testNull: null = null;
    testNumber: number | null = null;
    testUndefined: undefined | null = null;
    testDate: Date | null = null;
    testNullLiteral: NullLiteral | null = null;
    testBoolean: boolean | null = null;
    testAny: any | null = null;
    testInterface: Test2 | null = null;
    testPromise: Promise<string | number | null> | null = null;
    testMethod(arg: string | null): string | null {
        return arg;
    }
}
_ßr.Type.store.set(31, { n: "test3", k: 3, types: [
    _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }),
    _ßr.Type.store.wrap({ n: "number", k: 2, ctor: function () {return Promise.resolve(Number);} })
], union: true, inter: false });

_ßr.Type.store.set(99,  { n: "testDate", k: 3, types: [_ßr.Type.store.getLazy(99), _ßr.Type.store.wrap({ n: "null", k: 2 })], union: true, inter: false });
_ßr.Type.store.set(100, { n: "testNullLiteral", k: 3, types: [_ßr.Type.store.getLazy(100), _ßr.Type.store.wrap({ n: "null", k: 2 })], union: true, inter: false });
_ßr.Type.store.set(112, { n: "Promise", k: 2, args: [_ßr.Type.store.get(31)] });

_ßr.Type.store.set(95, { k: 1, n: "Test3", fn: "ts-api-test/test.ts:Test3#95", props: [
    { n: "testString1", t: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testString2", t: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testNull", t: _ßr.Type.store.wrap({ n: "null", k: 2 }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testNumber", t: _ßr.Type.store.wrap({ n: "number", k: 2, ctor: function () {return Promise.resolve(Number);} }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testUndefined", t: _ßr.Type.store.wrap({ n: "null", k: 2 }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testDate", t: _ßr.Type.store.get(99), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testNullLiteral", t: _ßr.Type.store.get(100), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testBoolean", t: _ßr.Type.store.wrap({ n: "boolean", k: 2, ctor: function () {return Promise.resolve(Boolean);} }), am: 2, acs: 0, ro: false, o: false },
    { n: "testAny", t: _ßr.Type.store.wrap({ n: "any", k: 2 }), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testInterface", t: _ßr.Type.store.get(88), am: 2, acs: 0, ro: false, o: false }, 
    { n: "testPromise", t: _ßr.Type.store.get(112), am: 2, acs: 0, ro: false, o: false }
], meths: [
    { n: "testMethod", params: [
        { n: "arg", t: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), o: false }
    ], rt: _ßr.Type.store.wrap({ n: "string", k: 2, ctor: function () {return Promise.resolve(String);} }), tp: [], o: false, am: 2 }
], ctors: [{ params: [] }], ctor: function () {return Promise.resolve(Test3);} });

As you can see, only types for testNull, testDate, testNullLiteral are shown as they should.


Also, is the behavior of testDate correct?

In testDate recursive reference specified to type testDate (_ßr.Type.store.getLazy(99))


tst-reflect: 0.7.5
tst-reflect-transformer: 0.9.10

Optional getType()

Suggestion

ts-reflection may be used in libraries and frameworks but not all the users want to use transformers or TypeScript at all. It would be nice to have optional getType() which return instance of Type or undefined.

Options:

  1. 👍
getType(): Type; // throws if undefined
getOptionalType(): Type | undefined;
  1. 👀
getType(): Type | undefined;
getTypeRequired(): Type; // throws if undefined
  1. 🚀
    Same as opt. 2. just name of the second function differ.
getType(): Type | undefined;
getRequiredType(): Type; // throws if undefined
  1. ❤️
    Introduce Null-object(/void) type, which will be empty, return false, undefined etc. as result of all merhods.
getType(): Type
class Type {
    static VoidType = new Type(.....);
}

User code example:

const t = getType<Foo>();

if (t == Type.VoidType) 
{
    throw new Error();
}

Example

class LoggerProvider {
    createLogger<TCategory = any>(categoryName?: string) {
        categoryName ??= getOptionalType<TCategory>()?.name;

        if (!categoryName) {
            throw new Error("Undefined category name.");
        }
    }
}

Another options? Votes?

Access to property modifiers

It could be nice to add access to property modifiers such as public, private, protected and readonly. Are you interested in a Pull Request?

TypeError: Cannot read properties of undefined (reading 'declarations') (monorepo Nx)

The project is a monorepo created with Nx.
I'm using ts-patch (ttypescript) to allow transformers globally on the project
Is this error related to the local private pkg?

Stack

ERROR in ./apps/api/src/app.module.ts
Module build failed (from ./node_modules/ts-loader/index.js):
TypeError: Cannot read properties of undefined (reading 'declarations')
    at processGenericCallExpression (/home/zzzzz/IdeaProjects/xxxx/node_modules/tst-reflect-transformer/dist/processGenericCallExpression.js:12:25)
    at mainVisitor (/home/zzzzz/IdeaProjects/xxxx/node_modules/tst-reflect-transformer/dist/visitors/mainVisitor.js:43:97)
    at Context._visitor (/home/zzzzz/IdeaProjects/xxxx/node_modules/tst-reflect-transformer/dist/contexts/Context.js:9:35)
    at visitNodes (/home/zzzzz/IdeaProjects/xxxx/node_modules/typescript/lib/typescript.js:87060:48)
    at Object.visitEachChild (/home/zzzzz/IdeaProjects/xxxx/node_modules/typescript/lib/typescript.js:87363:67)
    at mainVisitor (/home/zzzzz/IdeaProjects/xxxx/node_modules/tst-reflect-transformer/dist/visitors/mainVisitor.js:85:15)
    at Context._visitor (/home/zzzzz/IdeaProjects/xxxx/node_modules/tst-reflect-transformer/dist/contexts/Context.js:9:35)
    at visitNode (/home/zzzzz/IdeaProjects/xxxx/node_modules/typescript/lib/typescript.js:87007:23)
    at Object.visitEachChild (/home/zzzzz/IdeaProjects/xxxx/node_modules/typescript/lib/typescript.js:87634:115)
    at mainVisitor (/home/zzzzz/IdeaProjects/xxxx/node_modules/tst-reflect-transformer/dist/visitors/mainVisitor.js:85:15)

Affected line

processGenericCallExpression.js

    if (!fncType.symbol.declarations) {
        throw new Error("Unable to resolve declarations of symbol.");
    }

Affected file

import {Module} from '@nestjs/common';
import {AppController} from './app.controller';
import {AppService} from './app.service';
import {GraphQLModule} from '@nestjs/graphql';
import {ApolloDriver, ApolloDriverConfig} from '@nestjs/apollo';
import {join} from 'path'
import {TasksModule} from './task/task.module';
import {MongooseModule} from '@nestjs/mongoose';
import {RequestContextCreatorInterceptor, RequestContextModule} from "@privatepkt/nestjs-request-context";
import {APP_INTERCEPTOR} from "@nestjs/core";

@Module({
  imports: [
    RequestContextModule, // thread-local / asyncLocalStorage for the nest request
    TasksModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'apps/api/schema.gql'),
      sortSchema: true,
      definitions: {
        path: join(process.cwd(), 'apps/api/schema.ts'),
        outputAs: 'interface',
      },
      // TODO disable for prod
      introspection: true,
      debug: true,
      playground: true,
    }),
    // TODO use env var
    MongooseModule.forRoot('mongodb://localhost/db', {
      user: "xxx",
      pass: "xxx"
    })
  ],
  controllers: [AppController],
  providers: [AppService,
    {
      provide: APP_INTERCEPTOR,
      useClass: RequestContextCreatorInterceptor, // thread-local / asyncLocalStorage for the nest request, analyze request and fill data (graphql, http ect)
    }],
})
export class AppModule {
}

tsconfig.base,json

    "paths": {
      "@privatepkt/graphql-utils": [
        "libs/graphql-utils/src/index.ts"
      ],
      "@privatepkt/nestjs-request-context": [
        "libs/nestjs-request-context/src/index.ts"
      ]
    },
    ,
    "plugins": [
      {
        "transform": "tst-reflect-transformer"
      }
    ]

If you have suggestion on how to fix this let me know :).

Support of Enums

Given something like

  export interface Person {
    status: MaritalStatus;
  }

  enum MaritalStatus {
    Married = 0,
    Single = 1,
  }

Is there a way to get the enum name and literals while introspecting Person type property (getType<Person>().getProperties()[0])?

I see that the property type kind is Container which totally makes sense, but cannot find neither the enum name nor its value names.

Thanks!

Minify runtime package

For the sake of browser usage we should make bundled and minified version of runtime package which will be browser (plain TypeScript; no bundlers) friendly.

How to get properties of {[key in keyof T]: T[key]} type?

import { getType, Type } from "tst-reflect"
export class Temp {
    id: string
    email: string
}
type T = {
    [key in keyof Temp]: Temp[key]
}
function foo<T>() {
    const type: Type = getType<T>()
    console.log(type)
}
foo<T>()

Following code produces

this output
TypeActivator {
  _name: 'T',
  _fullName: 'T',
  _kind: 5,
  _constructors: [],
  _properties: [],
  _methods: [],
  _decorators: [],
  _typeParameters: [],
  _ctor: undefined,
  _ctorDesc: ConstructorImportActivator {},
  _interface: undefined,
  _isUnion: false,
  _isIntersection: false,
  _types: [],
  _literalValue: undefined,
  _typeArgs: [],
  _conditionalType: undefined,
  _indexedAccessType: undefined,
  _genericTypeConstraint: undefined,
  _genericTypeDefault: undefined,
  _baseType: TypeActivator {
    _name: 'Object',
    _fullName: 'Object',
    _kind: 2,
    _constructors: [],
    _properties: [],
    _methods: [],
    _decorators: [],
    _typeParameters: [],
    _ctor: [Function: ctor],
    _ctorDesc: ConstructorImportActivator {},
    _interface: undefined,
    _isUnion: false,
    _isIntersection: false,
    _types: [],
    _literalValue: undefined,
    _typeArgs: [],
    _conditionalType: undefined,
    _indexedAccessType: undefined,
    _genericTypeConstraint: undefined,
    _genericTypeDefault: undefined,
    _baseType: undefined
  }
}
As I can see it doesn't contain neither properties nor types. Is it bug or feature or do I use it in wrong way?

Does it work with Omit and Pick? (I see the same output for them because they are implemented in similar way I did it in example)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.