reflec-ts icon indicating copy to clipboard operation
reflec-ts copied to clipboard

Its not good idea to extends Interfaces , classes with reflector functions

Open stepancar opened this issue 7 years ago • 6 comments

Hello, thank you for this project.

Now we can write somthng like this

interface UiButtonProps {
    description: string;
}

for (let member of UiButtonProps.members) {
    let memberDescription = `Member ${member.name} is ${member.type.kind}`;
    if (member.type.kind === 'interface' || member.type.kind === 'class') {
        memberDescription += ' - name: ' + (<NamedType>member.type).name;
    }
    console.log(memberDescription);
}

But i think it's not good; Standard reflection framework just provide functions to reflect. And we can just pass Class or Interface to function;

If your aim - jsut provide intellisence for usage reflector functions - we can create any type like Type in .Net reflection

 interface Type {
     members: Array<any>,
     /...
 }
// and then 

import reflect from 'ts-reflect';
class MyTestClass{
}
reflect(MyTestClass).members.forEach( member=> console.log(member));

I know tha we can't pass interface to function cause it's typescript syntax limitation;

What do you think about it?

stepancar avatar Aug 26 '16 15:08 stepancar

Actually the solution you proposed in your second snippet is totally feasible, since it's a class:

reflect(MyTestClass).members.forEach( member=> console.log(member));

MyTestClass symbol is valid until emitting time because it's a constructor function. So, yes we would remove getClass() from Function interface and go with a module function. I'd like to listen other opinions about this. To me, SomeClass.getClass() sounds more fluent, but it's only an opinion :)

Instead, interfaces do not leave anything during emitting phase, so we must have something to handle their "references". We may use strings, but it's not so immediate, it's error prone and not refactor-friendly. What kind of issue you see about using MyInterface as a synthetic constant as it is now?

pcan avatar Aug 26 '16 15:08 pcan

Now , if we want to create any tool using typescript for reflection - we should to build it using your custom compiler. But i'ts not easy for most developers. Maybe reflector should be just a function which we can call passing path to source file or to project ? For example we have any ts project and we want to generate documentation for each classes which extended by ReactComponent class; example // MyComponents.ts

import * as React from 'react';
export class FirctComponent extends React.Component<{ member1: number }, {}>{

}

So, i suggest somthing like this MySuperDocsGenerator.ts / .js

import {reflector} from 'ts-reflector';
reflector('../projects/MySuperProject/src/MyComponents.ts').classes.filter( cl => cl.base.name === 'ReactComponent').map(cl =>  ( cl.base.genericsParams[0]).forEach(propsInterface => {
     propsInterface.members.forEach(member =>{
          console.log(member.name);
          console.log(member.comments);
     });
})

should print // member1

stepancar avatar Aug 26 '16 17:08 stepancar

Ok, now I got your point. I know that already exists some library that simply does mere type extraction from a typescript source. Actually this is not the scope of reflec-ts that instead aims to link classes (constructor functions) with their metadata seamlessly. I'll try to elaborate this concept better.

Let's suppose you have a class somewhere hidden in your code and a factory instantiates an object of this class. You are building a DI framework that needs to know if that object's class implements a specific interface because it has to decide at runtime which object inject into another, using interfaces. Here, the only way to get Class metadata for your object is through its object.constructor. Please note that at this point you don't know where this object has been created and, mainly, you don't have any means to access to the class literal class MySuperHiddenClass (that may be module-private, as stated before). So, in some way, the constructor has to bring some additional info with itself that allows you retrieve the Class metadata object (you may want object.constructor.getClass() or Reflection.getClass(object.constructor) but it's not relevant now). Basically, we have to assign an hidden field (preferably using a Symbol as key) to the constructor in order to retrieve its metadata (see Reflection.js, this is what actually happens).

Here, this is the point. Only a compiler can do this thing. Somewhere in the source code, in your super-private module, just after the MySuperHiddenClass class declaration, there must be something like MySuperHiddenClass[mySymbol] = metadataHook (or Reflection.registerClass(MySuperHiddenClass, metadataHook), this is what actually happens). And this is the only chance your code has to create a link between the metadata (that is always available through Reflection.js) and the super-hidden class. The user will never write this magic instruction just after every class. So the compiler comes in our help: during the parsing phase when it detects that you are typing class MySuperHi... on your keyboard, it's already preparing the next instruction Reflection.registerClass(MySuperHi..., metadataHook). When the code will be translated to JS and then executed, you will not notice anything strange or different about your class. It will be private as before, but someone will be keeping a reference to it. This someone is Reflection.js that will allow you to get that constructor also by its string name, if you want.

Let me know if something is not clear, I realized that this is a very complex topic, so feel free to ask and open more issues if you need. I'm happy to contribute to the TypeScript community.

P.S.: it should be not so difficult to use reflec-ts instead of the official TypeScript compiler: the API is the same, the tsc command is the same (just with different name, in order to distinguish it) with same options. Instead of including typescript as reference in your devDependencies you have to include reflec-ts. If there is something not well documented, please report an issue and I'll keep the docs updated. Thank you again for your interest :+1:

pcan avatar Aug 26 '16 20:08 pcan

I know that already exists some library that simply does mere type extraction from a typescript source.

I am interested in this project for the exact purpose of type extraction from interfaces for documentation purposes. I am not aware of any other tool that works with newer version of typescript. Which libraries are you referring to?

cvle avatar Oct 15 '16 14:10 cvle

Which libraries are you referring to?

You may want to try ts-type-info.

pcan avatar Oct 16 '16 14:10 pcan

Thanks, I'll check it out!

cvle avatar Oct 16 '16 16:10 cvle