-
-
Notifications
You must be signed in to change notification settings - Fork 676
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
Types transformation utils #453
Comments
For anyone else looking for this functionality now, I was able to hack together this partial type factory function by directly modifying the metadata storage object. Not ideal as this is a private api but it seems to do the trick so far. It just copies fields of the given type and creates a new type with the same fields, only all nullable. import { ClassType } from 'type-graphql'
// NOTE: This only works for object types, not input types
export function PartialType<E>(EntityClass: ClassType<E>): any {
const metadata = (global as any).TypeGraphQLMetadataStorage
class PartialClass {}
const name = `${EntityClass.name}Partial`
Object.defineProperty(PartialClass, 'name', { value: name })
// Create a new object type
const newObjectType = {
name,
target: PartialClass,
description: `Partial type for ${EntityClass.name}`
}
metadata.objectTypes.push(newObjectType)
// Copy relevant fields and create a nullable version on the new type
const fields = metadata.fields.filter(
f => f.target === EntityClass || EntityClass.prototype instanceof f.target
)
fields.forEach(field => {
const newField = {
...field,
typeOptions: { ...field.typeOptions, nullable: true },
target: PartialClass
}
metadata.fields.push(newField)
})
return PartialClass
} Use it like so: @ObjectType()
export class User {
@Field()
name: string
@Field()
email: string
// ...
}
export const UserPartial = PartialType(User) |
And for anyone else looking for pick/omit functionality now, I would recommend using mixin classes to compose bigger types, rather than picking/omitting from bigger types to create a small ones: |
Could something like Or create a separate decorator for Seems like it would be more powerful and concise than a |
I think that Also, this will give you false positive if you want to use the same class as the type in the mutation, so you may thing that the value will exist and in runtime it will be nullable. That's why it's better to describe it as |
Alright, makes sense. There should be a way to make certain fields required as well, a combination of both |
In that case I think that the mixins pattern makes more sense than trying to combine Partial and Required - it would be much more maintainable I think 😉 |
Here's a import { ClassType, InputType, ObjectType } from 'type-graphql';
export default function PartialType<TClassType extends ClassType>(
BaseClass: TClassType,
) {
const metadata = (global as any).TypeGraphQLMetadataStorage;
@ObjectType({ isAbstract: true })
@InputType({ isAbstract: true })
class PartialClass extends BaseClass {}
// Copy relevant fields and create a nullable version on the new type
const fields = metadata.fields.filter(
f => f.target === BaseClass || BaseClass.prototype instanceof f.target,
);
fields.forEach(field => {
const newField = {
...field,
typeOptions: { ...field.typeOptions, nullable: true },
target: PartialClass,
};
metadata.fields.push(newField);
});
return PartialClass;
} use like: @InputType()
export class SomethingInput extends PartialType(
SomethingModelBase,
) {} |
Be aware that the internal metadata storage might be changed without any notice between releases, so it's not recommended to do that kind of hacks. |
@MichalLytek I'm personally well aware. Hopefully you can include such functionality in the core soon, so we can get rid of the workaround. This is one of the main issues users would run into as soon as their API evolves into something CRUD-like. |
|
@andreialecu |
try this monster 😄 |
Oh that's worked :D thanks, I've already added |
Curious if there's been any progress on this? Also curious how I'd get involved in helping out with this if not, would be super useful for something I'm doing at the moment! |
It would be very useful to avoid code duplication if this is implemented in the library itself. |
Curious why you no longer use this |
Just citing @ChrisLahaye from #1295 (comment):
|
What do you guys think about support Partial with "deep" options so that the partial is applied into nested classes? |
The upcoming Prisma 2 integration (#217) will generate TS classes with TypeGraphQL decorators based on data model definition of Prisma schema.
But to allow for further customization, TypeGraphQL needs some operators to transform the generated classes into the user one. Mainly to hide some fields from database like user password, so we need a way to omit some fields of the base class or pick only some selected fields.
Proposed API have two approaches to apply the transformation:
This approach is for use cases where you still want to have access to the hidden fields in other field resolvers, like hidding array or rates but exposing average rating by the field resolver.
This approach is better when you want to create a derived type, like a subset of some input type, so you won't accidentally use the not existing field.
Initial version might not support inheritance or have a prototype methods leaks, because it's not needed by the Prisma integration. In the next release cycle I will try to make it work with broader range of use cases.
Later, more types transformation utils will be implemented, like
Partial(Foo)
for making fields optional,Required(Foo)
for making fields non-nullable.If possible, maybe I will try to add even a mapping util that will map the keys of one type to new values in a new type. For example if you want to generate a sorting input (with field types only ASC/DESC) based on an object type which fields are representing database columns 😉
The text was updated successfully, but these errors were encountered: