【问题标题】:How to use dynamic object properties in TypeScript如何在 TypeScript 中使用动态对象属性
【发布时间】:2018-08-13 19:14:08
【问题描述】:

我正在使用 TypeScript,我需要处理动态对象属性。我希望我能成为 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' }]
}

但是从上面显示的内容来看,我想让用户可以使用他自己的界面。因此,假设他想使用field 而不是columnId,而不是searchTerm,他将使用value。所以他的自定义界面是

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

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

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

但是我如何将 2 连接在一起?如何使用用户的CustomFilter 和FilterStruct。以下不起作用,我知道它不正确。

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

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

【问题讨论】:

  • 当您说“用户可以输入自己的自定义界面”时,您的意思是在运行时给予他们这种可能性吗?
  • 啊抱歉,我的意思是在他们的代码中,因为他们想要创建自己的界面,而不是在运行时。

标签: javascript typescript types interface typescript2.0


【解决方案1】:

您可以将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);

【讨论】:

  • 哇,我永远不会自己发现这个。这与我预期的不同,现在我对如何在我的代码中循环这个有点困惑?我是否必须做某种instanceof 才能确定它何时使用CustomFilter?考虑到它可能有/没有CustomFilter,你介意举个例子来说明如何循环,这会很有帮助。
  • @ghiscoding 添加了用法示例,filters 只是一个数组,一切正常,只是有一些额外的属性。
  • 非常感谢,TypeScript 很棒,我想确保正确使用它:)
猜你喜欢
  • 1970-01-01
  • 2021-11-27
  • 2020-10-07
  • 2019-08-23
  • 2021-06-03
  • 2021-09-29
  • 2021-02-27
相关资源
最近更新 更多