-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
also emit type arguments with --emitDecoratorMetadata #3015
Comments
How are using the type information later on? Generics do not really have a runtime representation. So other than Array, how would the type parameter be used? Also what about generic classes, how should OneRelation be represented in class Article. |
declare class OneRelation<T> {
fetch(): T;
}
@Collection('users')
class User {
email: string;
username: string;
} In this case the type parameter would be used by the model framework to hook up the correct model (User) to this relation. For example At the moment i would have to repeat myself to let the framework know the target model like so: @Collection('articles')
class Article {
title: string;
content: string;
@Relation(User)
author: OneRelation<User>;
} What i would like to do is avoid specifying the User twice and let the framework to be smart and infer it from the type argument: @Collection('articles')
class Article {
title: string;
content: string;
@Relation
author: OneRelation<User>;
} I don't see how the arguments passed to generics have any less runtime representation than any other types in typescript. I realise that interfaces don't have runtime representation and they are just emitted like this: __metadata('design:type', Object) Which does not give much info tbh but thats a different subject. I think emitting the type arguments would be useful as it makes possible the frameworks to infer more type info from the type itself rather than needing to duplicate it via decorator. I think thats the reason why the |
EmitDecoratorMetadata emits values, and not serialized types. So it only works with classes and native types. This is because classes act as both a type at design time and a factory at run time (I.e. Constructor function). And native types have matching boxed types that can be used as constructors at runtime as well. Interfaces as you noted do not have this duality. Generic types have no parallel at runtime, they are mearly a design time construct that allows for describing type relations. Similar to interfaces. The code sample above will assume something about the type OneRelationship, and in effect treats the generic type argument as an argument to the decorator. I believe what you are asking for is a full type serialization thst is available at runtime. Got this I do not see why interfaces should be excluded. @rbuckton had a proposal for this; @rbuckton can you share your type serialization proposal here? |
Full type serialization would be awesome and would definitely solve my case. |
Full type information on runtime, specifically generic parameters, would probably allow to build smart factory methods. That assumes also decorated method calls. I guess lot could be learned from type erasure mistakes in Java. |
+1 for full type information at runtime. There are many cool things that we'll be able to do with these. |
@pavelsavara, @dsebastien: I've been considering this, but truly capturing full type information would require not only compiler support but a full runtime library. I have an updated gist with a tentative JSON schema for type information. There are still many issues to consider, such as the fact that TypeScript uses structural typing for interfaces and assignability, so its generally not enough to state "Inject an IMyService instance into this constructor parameter", if you have a class with the same shape that is not an In general, strings or symbols will likely always be a better choice for use cases like dependency injection as they are easier to work with, are more reliable, and don't have the runtime overhead of a runtime library for interpreting type relationships. |
As I said before, the IoC usecase could be satisfied with only type name emmited by compiler. Structural interface inference at runtime is not really necessary, rather nice to have. Do you plan to add support for interface name emmision for constructor parameters ? Or perhaps add extension points to the compiler service to enable it ? |
I do not think we will emit type names, just because they do not make much sense in a structural type system. emitter extensibilty is something that is on our radar. |
@rbuckton when considering a format for JSON type information and a library to work with it, take a look at these two projects: Generates type information in JSON format: Library for working with type information in Node: |
I see type metadata emission as useful when wanting to infer things at runtime, for example generating a data model or serialize data. I really hope that the community can come up with a proposal that also works with decorators in cases where there are no type annotations available, like in standard ECMAScript. |
My notes say "Assign to Mohamed" |
The current design leverages JavaScript objects that exist at runtime. this works for classes, since they exist both in the type-space at compile-time and in the value-space at run-time. interfaces do not have these properties, nor do type parameters. Moreover, the name of an interface, or a type parameter by itself is not sufficient information in a structural type system. You need the shape of the type to be able to make any decisions. I definitely see the value in a full reflection system, that allows for querying types at runtime, asserting them, detecting compatibility using the same rules as the compiler use at design-time etc.. I would see this implemented through a JSON-like serialization mechanism similar to @rbuckton's proposal, and a runtime library that allows for querying these types and verifying assignability, subtype, and identity relationships, and figuring out type parameters, and constraints.. etc.. This however is a big undertaking that is out of scope for typescript at the current time. |
@mhegazy I understand that this is out of scope for TypeScript compiler itself, but it could expose at least some API that describe the type system used internally for type checking during compilation, so a third party post-compiler could implement a full-blown reflection system along with a runtime library (maybe using the JSON structure proposed by @rbuckton). |
You should be able to use the TypeChecker API from the compiler to get to the type information you need. there is no serialization logic readily available, but should be possible to add one. Here is the TypeChecker API: https://github.com/Microsoft/TypeScript/blob/085f0df4556801c14a4333d6c1d0bab1375586e4/lib/typescript.d.ts#L1027-L1057 Documentation on using the compiler API: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API Here is the part of the emitter that emitts this infromation today using the Checker: |
@mhegazy as I understood, it's impossible to override this function |
you could write your transform that transforms the decorator as a whole; but meta-data only is not overridable at the moment. |
@mhegazy update: |
tried to write switch (location.kind) {
case 264 /* SourceFile */:
if (!ts.isExternalOrCommonJsModule(location))
break;
isInExternalModule = true;
case 232 /* ModuleDeclaration */:
var moduleExports = getSymbolOfNode(location).exports;
//getSymbolOfNode(location) is undefined there.
// Location - sourcefile
// getSymbolOfNode(location.original) returns symbol
if (location.kind === 264 /* SourceFile */ || ts.isAmbientModule(location)) { Maybe this api didn't supposed to modify nodes (before typescript transformer)? upd: (<any>newNode).symbol = (<any>source).symbol; but this is really hacky, are there any ts function to do this? |
At the moment:
emits:
My suggestion is that that instead of just type it would also include the type args.
Maybe something like this:
With the latter i don't have to repeat myself in decorator that this is a one relation to user as i have already specified this in type.
Thanks,
Reio
The text was updated successfully, but these errors were encountered: