Как мне ввести рекурсивную функцию, получающую массив общих типов объединения? (typescript)

#javascript #typescript #typescript-generics

#javascript #typescript #typescript-generics

Вопрос:

Я пишу функцию, которая будет перебирать ряд объектов, каждый из которых содержит массив. Каждый элемент в массиве может иметь condition функцию, которая проверяет, следует ли его добавлять.

После некоторых попыток и чтения общих терминов я все еще застрял!

Ошибки, которые я вижу: 'Property 'condition' / 'items' / 'sections' does not exist on type 'T'

Кто-нибудь может помочь мне сделать эту работу? Спасибо!

Типы

 export interface ContractDetailBO {
  sections: ContractSectionBO[];
  condition?: Function;
}

export interface ContractSectionBO {
  items: ContractSectionItemBO[];
  condition?: Function;
}

export interface ContractSectionItemBO {
  label: string;
  condition?: Function;
}




export interface ContractBO {
  encryptedContractId: string;
}

export type AnyBO = ContractDetailBO | ContractSectionBO | ContractSectionItemBO;
 

Рекурсивная функция

 
export const isOfType = <T>(
  varToBeChecked: any,
  propertyToCheckFor: keyof T
): varToBeChecked is T =>
  (varToBeChecked as T)[propertyToCheckFor] !== undefined;

export function populate<T extends AnyBO>(inputArray: T[], config: Config): T[] {
    const returnItems: T[] = [];
    inputArray.map((item: T) => {
        // include this item if the condition passes or is not defined
        if (item.condition === undefined || item.condition(config)) {
            const itemToPopulate = { ...item };

            // check type of item so we know the next array to iterate

            if (isOfType<ContractDetailBO>(item, 'sections')) {
                const nextArray: ContractSectionBO[] = populate(item.sections, config);
                itemToPopulate.sections = nextArray;
            }
            if (isOfType<ContractSectionBO>(item, 'items')) {
                const leaves: ContractSectionItemBO[] = populate(item.items, config);
                itemToPopulate.items = leaves;
            }
            returnItems.push(itemToPopulate);
        }
    });
    return returnItems;
}

const populatedBOs:ContractDetailBO = populate(myContractDetailBO, myContractBO)
 

Ответ №1:

Вы можете попробовать это.

 
export type Config = {};

export interface BaseBO {
    condition?: (config: Config) => boolean;
}

export interface ContractDetailBO extends BaseBO {
    sections: ContractSectionBO[];
}

export interface ContractSectionBO extends BaseBO {
    items: ContractSectionItemBO[];
}

export interface ContractSectionItemBO extends BaseBO {
    label: string;
}

export type AnyBO = ContractDetailBO | ContractSectionBO | ContractSectionItemBO;

function isContractDetailBO(target: AnyBO): target is ContractDetailBO {
    return (target as ContractDetailBO).sections != null;
}
function isContractSectionBO(target: AnyBO): target is ContractSectionBO {
    return (target as ContractSectionBO).items != null;
}

export function populate<T extends AnyBO>(inputArray: T[], config: Config): T[] {
    const returnItems: T[] = [];
    for (const item of inputArray) {
        // include this item if the condition passes or is not defined
        if (!item.condition || item.condition(config)) {
            const itemToPopulate = { ...item };

            // check type of item so we know the next array to iterate

            if (isContractDetailBO(itemToPopulate)) {
                const nextArray: ContractSectionBO[] = populate(itemToPopulate.sections, config);
                itemToPopulate.sections = nextArray;
            }
            if (isContractSectionBO(itemToPopulate)) {
                const leaves: ContractSectionItemBO[] = populate(itemToPopulate.items, config);
                itemToPopulate.items = leaves;
            }
            returnItems.push(itemToPopulate);
        }
    }
    return returnItems;
}
 

Комментарии:

1. Большое спасибо @givi, это отлично работает 🙂 Я все еще чувствую, что слишком сложно подхожу к своей проблеме, но это отличное решение. Спасибо!