代码之家  ›  专栏  ›  技术社区  ›  ghiscoding

如何在TypeScript中使用动态对象属性

  •  1
  • ghiscoding  · 技术社区  · 7 年前

    我正在使用TypeScript,需要处理动态对象属性。我希望我能成为打字高手,但我不是。我的问题是,在我当前的项目中,我想让用户能够将自己的自定义接口输入到通用接口。

    我现在要做的是以下界面

    export interface Filter {
      columnId: string;
      searchTerm: string;
    }
    export interface GridState {
      filters: Filter[];
    }
    
    // use it as an array of filters
    const state: GridState = {
      filters: [{ columnId: 'firtName', searchTerm: 'John' }]
    }
    

    然而,从上面显示的内容来看,我想让用户可以使用自己的界面。所以让我们这样说,而不是 columnId ,他想使用 field 而不是 searchTerm 他会用 value 。所以他的自定义界面是

    export interface CustomFilter {
      field: string;
      value: string;
    }
    

    我想向用户提供的是这样一个自定义结构模板

    export interface FilterStruct {
      propNameColumnId: string;
      propNameSearchTerm: string;
    }
    

    但是我如何将这两个连接在一起呢?如何使用用户的 CustomFilter 使用 FilterStruct 。以下内容不起作用,我知道这是不正确的。

    export interface GridState {
      filters: FilterStruct<CustomFilter>[];
    }
    

    最终目标是用户能够使用自己的界面和属性名称进行输入。然后,在我这边,我将使用提供的动态对象属性循环遍历数组。

    1 回复  |  直到 7 年前
        1
  •  2
  •   Titian Cernicova-Dragomir    7 年前

    你可以 GridState 泛型,并为泛型参数提供默认值。而且 FilterStruct 可以继承 Array ,因此我们可以使用助手函数向数组添加额外属性:

    export interface FilterStruct<T> extends Array<T> {
        // We make sure the property names are actually properties of T
        // We make these optional you should use default values if they are undefined 
        // We do this to keep initialization simple in the non customized scenario, you can make them mandatory, but then you can't initialize with a simple array
        propNameColumnId?: keyof T; 
        propNameSearchTerm?: keyof T;
    }
    export interface Filter {
        columnId: string;
        searchTerm: string;
    }
    export interface CustomFilter {
        field: string;
        value: string;
    }
    // T has a default value of Filter so we don't have to specify it, unless we want to customize
    export interface GridState<T = Filter> {
        filters: FilterStruct<T>;
    }
    // Helper function to create an array with the extra properties
    function createFilterStruct<T>(cfg: { propNameColumnId: keyof T; propNameSearchTerm: keyof T; }, items: T[]) {
        return Object.assign(items, cfg);
    }
    
    // Default we can use simple array initailization
    const state: GridState = {
        filters: [{ columnId: 'firtName', searchTerm: 'John' }]
    }
    
    // Custom filter, create with createFilterStruct
    const stateCustom: GridState<CustomFilter> = {
        filters: createFilterStruct({ propNameColumnId: 'value', propNameSearchTerm: 'field' }, [
            { value: 'firtName', field: 'John' }
        ])
    }
    
    //Usage
    function loopThrough<T>(grid: GridState<T>){
        // Provide defaults for propNameColumnId and propNameSearchTerm
        let propNameColumnId = grid.filters.propNameColumnId || 'columnId' as keyof T
        let propNameSearchTerm = grid.filters.propNameSearchTerm || 'searchTerm' as keyof T
    
        // Loop throught the array normally, it is just an array
        for(let filter of grid.filters){
            // Access the properties
            console.log(`${filter[propNameColumnId]} = ${filter[propNameSearchTerm]}`);
        }
    }
    
    loopThrough(stateCustom);
    loopThrough(state);