MichalLytek / MichalLytek/type-graphql
Enhanced types reflection system
@MichalLytek is already working on this.
Since Mar 30, 2019.
- Dominant language
- TypeScript
- Stars
- 8.1k
- Forks
- 672
- PR merge metrics
- No merged PRs in 30d
Description
Introduction words
TypeGraphQL was created mainly to reduce the duplication in declaring GraphQL schema types and TypeScript types by synchronizing them in one source of truth using decorators and reflection magic.
Problem description
Probably the most annoying thing in TypeGraphQL is still the types duplication:
@Field(type => [String]) // <- here we have the TypeGraphQL type hint
propertyName: string[]; // <- and here we have the TS type
The even worse thing is that this types can be silently mismatched:
class Test {
@Field(() => [Author]) // <- wrong item type
actors: Actor[];
@Field() // <- required in schema
optionalField?: string; // <- optional in TS
}
Typed decorators (#221) are unfortunately only a partial solution for providing enhanced type-safety.
The root cause
The main cause of this situation is the limited limited reflection capabilities of TypeScript emitDecoratorMetadata option - it doesn't work with generics (string[], Array<number>, Promise<Recipe>) and it's not even able to detect optional/nullable fields (?: string, : number | null).
In the last few weeks I was working on a TypeScript compiler plugin that can enhance the built-in, limited reflection capabilities. The goal is to remove the need of providing the type => [String] hints by emitting full type info about properties type and methods return type.
Solution for the problem
The Proof of Concept is available in the separate repository:
https://github.com/19majkel94/typegraphql-reflection-poc
For now it can read and emit basic type info - primitives (like boolean, string), nullable types (?: string, : number | null), arrays (string[], Array<number>) and promises (Promise<Recipe>) as well as their combinations like Promise<Array<string | null> | null>.
So just by declaring TypeScript types:
@ObjectType
export class Sample {
@Field
dateField!: Date;
@Field
optionalStringField?: string;
@Field
nullableStringArrayField!: Array<string | null>;
@Field
nullableStringNullableArrayPromiseField!: Promise<Array<string | null> | null>;
}
It's possible to read their type info in runtime and generate this GraphQL schema type:
type Sample {
dateField: Date!
optionalStringField: String
nullableStringArrayField: [String]!
nullableStringNullableArrayPromiseField: [String]
}
The only drawback is that you have to use an alternative compiler, like ttypescript to run the transform plugin and emit enhanced type metadata. But as it integrates well with ts-node (ts-node -C ttypescript src/example/index.ts), I think that it's not so bad solution.
Future work
That proof of concept supports only basic subset of types. There is still a lot of work to be done (i.a. detecting args in methods, inline unions, generic types support) - you can find more info here.
If you love this idea but you can't help with the code by making some contributions, please consider supporting the project and our development efforts! ❤️
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.
