All files / src/classes/decorators FieldConfigDecorator.ts

92.3% Statements 12/13
85.71% Branches 6/7
100% Functions 4/4
92.3% Lines 12/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79          1x                             5x                                       1x 5x   5x                       5x 4x       5x     5x 1x       5x     5x              
import PrjBaseData from 'src/models/Data/PrjBaseData';
 
/**
 * A unique symbol used to mark properties that should be included in the {@link PrjBaseData.mergeData} method.
 */
export const FieldConfigSymbol: unique symbol = Symbol('FieldConfig');
 
/**
 * Represents the field configuration properties.
 */
interface IFieldConfig_ {
    [FieldConfigSymbol]?: IFieldConfigEntry[];
}
 
/**
 * Type guard to check if an object is an instance of {@link IFieldConfig_}.
 * @param obj The object to check.
 * @returns Ever `True` because the {@link FieldConfigSymbol} property is optional.
 */
function isIFieldConfig_(obj: unknown): obj is IFieldConfig_ {
    return true;
}
 
/**
 * Represents a entry in the field configuration.
 */
interface IFieldConfigEntry {
    key: string | number | symbol;
    defaultValue?: unknown;
}
 
/**
 * A decorator function to mark class properties for inclusion in the {@link PrjBaseData.mergeData} method.
 * @param defaultValue The optional default value for the field. The setter will be used with this value.
 * @returns A decorator function.
 * @remarks - The field must have a getter and setter.
 * - The field must be marked as `@fieldConfig` in the class.
 * - `@fieldConfig` in higher classes will overwrite the default value of the same field in lower classes.
 * - Create a {@link FieldConfigSymbol} property in the class to get the field configurations.
 */
export function fieldConfig(defaultValue?: unknown) {
    return function (target: unknown, propertyKey: string | symbol): void {
        // Check if the target is an object guard the optional `FieldConfigSymbol` property.
        Iif (
            !target ||
            typeof target !== 'object' ||
            !(target instanceof Object) ||
            !isIFieldConfig_(target.constructor)
        ) {
            throw new Error(
                'The fieldConfig decorator can only be used on class properties.',
            );
        }
 
        // If the class does not have a `FieldConfigSymbol` property, create it.
        if (!target.constructor[FieldConfigSymbol]) {
            target.constructor[FieldConfigSymbol] = [];
        }
 
        // Get the field configurations for the class.
        const fieldConfigs = target.constructor[FieldConfigSymbol];
 
        // Check if the field is already in the list and..
        const existingIndex = fieldConfigs.findIndex(
            (config: IFieldConfigEntry) => config.key === propertyKey,
        );
 
        // ..if the field is not in the list, add it.
        if (existingIndex == -1) {
            // The first entry is the highest class,
            // allowing derived classes to overwrite standard values.
            fieldConfigs.push({
                key: propertyKey,
                defaultValue,
            });
        }
    };
}
 
Zur TypeDoc-Dokumentation