将ID分配给ID属性名称可配置的通用类型?

时间:2018-09-21 22:02:29

标签: javascript typescript

我有一个通用类型E。它是诸如CustomerProductTodo等的Entity实例。具有两个可配置字段idguid,但是不必将其命名为。这两个属性的名称是可配置的。

这是一个实体的示例:

class Todo {
    id: string;
    gid: string;
    title: string;
    completed: boolean;
}

为了配置属性名称,使用了这样的配置类:

    /**
    * Store configuration.
    */
    export class StoreConfig {
    idKey?:string ='id';
    guidKey?:string ='gid';
    }

StoreConfig用于确定分配了生成ID的类型E的实例上属性的名称。所以我们有这样的方法:

/**
* Post (Add a new) element to the store.
* @param e
*/
post(e: E) {  
    const guid: string = v1();
    e[this.config.guid] = guid;
    this.entries[guid] = e;

打字稿抛出:

  

[ts]元素隐式地具有“ any”类型,因为类型“ {}”没有索引签名    (属性)EStore.config:StoreConfig

是否可以解决此问题?这是个小把戏,因为E类型具有两个属性,整体名称是可配置的...?

该类本质上也有两个索引签名。一个用于id属性(还没有命名/它是可配置的),另一个是guid属性(还没有命名/它是可配置的)...

如果我这样做:

    (<any>e)[this.config.guidKey] = guid;

Typescript不会抱怨,但是我想知道是否有一种更优雅的方式来声明它?

1 个答案:

答案 0 :(得分:1)

You'll need type parameters for the names of the two properties. (Well, since the properties have the same type string, you could use a single type parameter with a union type, but that's a hack IMO.) Then you can use a mapped type to declare the actual properties.

interface StoreConfig<I extends string, G extends string> {
    idKey: I;
    guidKey: G;
}

type EntityBase<I extends string, G extends string> =
    // TODO: In TypeScript 3.1, change to the following for elegance:
    //{[P in I]: string} & {[P in G]: string}
    {[P in I | G]: string};

class Store<I extends string, G extends string, E extends EntityBase<I, G>> {
    constructor(private config: StoreConfig<I, G>) {}

    entries: {[guid: string]: E};

    post(e: E) {  
        const guid: string = v1();
        e[this.config.guidKey] = guid;
        this.entries[guid] = e;
    }
}

// https://github.com/Microsoft/TypeScript/issues/10571 will make this nicer.
new Store<"id", "gid", Todo>({idKey: "id", guidKey: "gid"});