diff --git a/README.md b/README.md index 6c55991..2a60609 100644 --- a/README.md +++ b/README.md @@ -83,18 +83,22 @@ input UserInput3 { **`names`**: The fields to transform. A potentially optional object containing the names of the fields as key, e.g. `{ id: 1, name: 1 }`. The TypeScript type enforces all values to be *1*, but the value isn't actually used. We just need the names as object to determine if a name is included in constant time. -### `Pick(BaseClass, names)` +**`options`**: The transformation options. An optional object containing any of the following properties: + +- `directives`: A boolean that indicates whether to apply directives. By default, false. + +### `Pick(BaseClass, names, [options])` Constructs a type by picking the keys of `names` from `BaseClass`. -### `Omit(BaseClass, names)` +### `Omit(BaseClass, names, [options])` Constructs a type by picking all fields from `BaseClass` and then removing the keys of `names`. -### `Partial(BaseClass, [names])` +### `Partial(BaseClass, [names], [options])` Constructs a type by picking all fields from `BaseClass` and then setting the keys of `names` to optional. The opposite of Required. By default, `names` contains all names. -### `Required(BaseClass, [names])` +### `Required(BaseClass, [names], [options])` Constructs a type by picking all fields from `BaseClass` and then setting the keys of `names` to required. The opposite of Partial. By default, `names` contains all names. diff --git a/lib/index.ts b/lib/index.ts index 5908508..c9d04c3 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -23,9 +23,14 @@ export function buildEnum( return enumObj; } +export interface Options { + directives?: boolean; +} + export function buildType( BaseClass: ClassType, buildFn: (f: FieldMetadata) => FieldMetadata | FieldMetadata[] | undefined, + options?: Options, ): any { @InputType({ isAbstract: true }) @ObjectType({ isAbstract: true }) @@ -47,21 +52,33 @@ export function buildType( } }); + if (options?.directives) { + const fieldNames = metadata.fields.filter((f) => f.target === ChildClass).map((f) => f.name); + + metadata.fieldDirectives.forEach((f) => { + if (f.target !== BaseClass && !f.target.isPrototypeOf(BaseClass)) return; + + if (fieldNames.includes(f.fieldName)) { + metadata.fieldDirectives.push({ ...f, target: ChildClass }); + } + }); + } + return ChildClass; } -export function Pick>(BaseClass: T, names: Record): ClassType, K>> { - return buildType(BaseClass, (f) => f.name in names ? f : undefined); +export function Pick>(BaseClass: T, names: Record, options?: Options): ClassType, K>> { + return buildType(BaseClass, (f) => f.name in names ? f : undefined, options); } -export function Omit>(BaseClass: T, names: Record): ClassType, K>> { - return buildType(BaseClass, (f) => f.name in names ? undefined : f); +export function Omit>(BaseClass: T, names: Record, options?: Options): ClassType, K>> { + return buildType(BaseClass, (f) => f.name in names ? undefined : f, options); } -export function Partial = keyof InstanceType>(BaseClass: T, names?: Record): ClassType, K>> { - return buildType(BaseClass, (f) => !names || f.name in names ? { ...f, typeOptions: { ...f.typeOptions, nullable: true } } : f); +export function Partial = keyof InstanceType>(BaseClass: T, names?: Record | null, options?: Options): ClassType, K>> { + return buildType(BaseClass, (f) => !names || f.name in names ? { ...f, typeOptions: { ...f.typeOptions, nullable: true } } : f, options); } -export function Required = keyof InstanceType>(BaseClass: T, names?: Record): ClassType, K>> { - return buildType(BaseClass, (f) => !names || f.name in names ? { ...f, typeOptions: { ...f.typeOptions, nullable: false } } : f); +export function Required = keyof InstanceType>(BaseClass: T, names?: Record | null, options?: Options): ClassType, K>> { + return buildType(BaseClass, (f) => !names || f.name in names ? { ...f, typeOptions: { ...f.typeOptions, nullable: false } } : f, options); }