类构造函数的参数中的Typescript泛型类型

时间:2019-02-11 18:58:39

标签: typescript

我正在尝试使用facebook dataloader实现存储库通用模式。

此代码用于GraphQL API。

抽象基类,是否出现错误:

import { Collection, Db, InsertOneWriteOpResult, ObjectId } from "mongodb";
import { RepositoryGeneric } from "../../types/database";

export default abstract class BaseRepository<TEntity, TLoaders> implements RepositoryGeneric<TEntity> {
    protected mongoCollection: Collection<TEntity>;
    protected dataLoaders: TLoaders;

    public constructor(database: Db, collection: string, loaders: TLoaders) {
        this.mongoCollection = database.collection(collection);
        this.dataLoaders = loaders;
    }

    public async findOne(id: ObjectId | string): Promise<TEntity | null> {
        if (typeof id === "string") {
            id = ObjectId.createFromHexString(id);
        }

        // This line is producing the error
        return this.dataLoaders.id.load(id);
    }

    public async create(entity: TEntity): Promise<TEntity> {
        const result: InsertOneWriteOpResult = await this.mongoCollection.insertOne(entity);

        if (!result.result.ok) {
            throw new Error();
        }

        return result.ops[0];
    }
}

扩展抽象基类的类:

import { MongoClient } from "mongodb";
import { EntityBusiness } from "../../types/database";
import BusinessLoaders from "../loaders/businessLoaders";
import BaseRepository from "./baseRepository";

export default class BusinessRepository extends BaseRepository<EntityBusiness, BusinessLoaders> {
    public constructor(mongoClient: MongoClient, businessLoaders: BusinessLoaders) {
        super(mongoClient.db(), "business", businessLoaders);

        // tslint:disable-next-line: no-console
        console.log(businessLoaders.id);
    }
}

实现数据加载器的类:

import DataLoader from "dataloader";
import { AggregationCursor, Collection, MongoClient, ObjectId } from "mongodb";
import { EntityBusiness, LoaderCommon } from "../../types/database";

export default class BusinessLoaders implements LoaderCommon<EntityBusiness> {
    private idLoader: DataLoader<ObjectId, EntityBusiness>;

    public constructor(mongoClient: MongoClient) {
        this.idLoader = new DataLoader((keys) => this.idBatch(mongoClient, keys), {
            cacheKeyFn: (key) => key.toHexString(),
        });
    }

    public get id(): DataLoader<ObjectId, EntityBusiness> {
        return this.idLoader;
    }

    private async idBatch(mongoClient: MongoClient, keys: ObjectId[]): Promise<EntityBusiness[]> {
        const collection: Collection<EntityBusiness> = mongoClient.db().collection("business");
        const aggregation: AggregationCursor<EntityBusiness> = collection.aggregate([
            { $match: { _id: { $in: keys } } },
            { $addFields: { __order: { $indexOfArray: [keys, "$_id"] } } },
            { $sort: { __order: 1 } },
            { $project: { __order: 0 } },
        ]);

        return aggregation.toArray();

    }
}

我在打字稿方面没有太多经验,但我希望不会有任何错误。相反,我得到了错误:

  

错误TS2339:类型“ TLoaders”上不存在属性“ id”。

1 个答案:

答案 0 :(得分:0)

您将需要指定您的通用类型具有一些约束,特别是它具有id属性。您可以使用通用类型定义中的extends关键字来完成此操作:

interface ILoadersInterface<TEntity> {
  id: DataLoader<ObjectId, TEntity | null>
}

export default abstract class BaseRepository<TEntity, TLoaders extends ILoadersInterface<TEntity>> 
  implements RepositoryGeneric<TEntity> {
    // ...
}

更一般而言,一个类需要在其内部具有足够的类型信息才能正确解决所有代码使用情况,因为在这种情况下,TypeScript无法通过使用情况推断出TLoaders将具有一个id属性。从概念上讲,除非您指定诸如unknownextends Interface等约束,否则您可以想象泛型类型参数大致为extends string