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

是否可以用仍然强制类型安全的方式编写泛型?

  •  0
  • MusicDev  · 技术社区  · 6 年前

    假设我有多个具有类似方法的类。在本例中,一组具有saveModel方法的服务:

    public async saveModel(newModel: IModel): Promise<IModel> {
        return await newModel.save();
    }
    

    我创建的通用方法如下所示:

    public async saveModel<P extends Document>(newModel: P): Promise<P> {
        return await newModel.save();
    }
    

    我的所有服务(使用该方法的服务)都扩展了一个包含该泛型方法的类,所以现在在这些服务中,我可以调用 answerService.saveModel(newAnswer) 这样就省去了答案。所有的东西都很漂亮,直到我意识到我也可以在里面放一个IQuestion类型的东西。如果我愿意,还有一个IDinosaur类型的对象,只要它扩展了Mongoose文档接口。

    我有没有办法强制每个服务中使用某些接口?如何确保answerService只保存IAnswer类型的对象?answerService是否需要实现一个具有以下saveModel签名的接口:

    interface IAnswerService {
        saveModel(newModel: IAnswer): Promise<IAnswer>;
    }
    

    这是整件事的基本情况:

    实体服务.ts

    import { Document } from 'mongoose';
    export class EntityService implements IEntityService {
      public async saveModel<P extends Document>(newModel: P): Promise<P> {
        try {
          const savedModel = await newModel.save();
          return savedModel;
        } catch (err) {
          console.log(err);
          return null;
        }
      }
    }
    

    应答服务

    import { Answer, IAnswer } from '@models/answer.model';
    import { EntityService } from '@classes/EntityService';
    
    class AnswerService extends EntityService {
      private static instance: AnswerService;
    
      private constructor() {
        super();
      }
    
      static getInstance(): AnswerService {
        if (!AnswerService.instance) {
          AnswerService.instance = new AnswerService();
        }
    
        return AnswerService.instance;
      }
    }
    
    const answerService = AnswerService.getInstance();
    export default answerService;
    

    答案.model.ts

    import mongoose, { Schema, Document, Model } from 'mongoose';
    
    export interface IAnswer extends Document {
      answerText: string;
      posterID: string;
    }
    
    const AnswerSchema: Schema = new Schema({
      answerText: {type: String, required: true},
      posterID: {type: String, required: true}
    }, {
      minimize: false
    });
    
    export const Answer: Model<IAnswer> = mongoose.model<IAnswer>('Answer', AnswerSchema);
    

    所有其他接口都继承自IAnswer所做的相同事情(模型、模式、文档)。

    AnswerService的用法通常如下所示:

    import answerService from 'answer.service';
    import { Answer } from 'answer.model';
    
    const answer = new Answer({
        answerText: 'Stuff',
        posterID: '123456789'
    })
    
    answerService.saveModel(answer);
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   jcalz    6 年前

    据我所知,你想要 EntityService 处理的特定子类型 Document . 如果是,那你就不想 saveModel() 通用的 P extends Document ;你想要整个 实体服务 类的泛型 P . 以及 实体服务 应该指定泛型类型参数。例如:

    // P moved to EntityService from saveModel    
    export class EntityService<P extends Document> {
      public async saveModel(newModel: P): Promise<P> {
        try {
          const savedModel = await newModel.save();
          return savedModel;
        } catch (err) {
          console.log(err);
          return null!;
        }
      }
    }
    
    // subclass sets specific value for P   
    class AnswerService extends EntityService<IAnswer> {
      private static instance: AnswerService;   
      private constructor() {
        super();
      }    
    }
    

    以后,如果你有 answer 类型 IAnswer 以及 document 只知道是类型 文件 ,然后是 AnswerService 只允许你 saveModel(answer) 而不是 saveModel(document) :

    AnswerService.getInstance().saveModel(answer); // okay
    AnswerService.getInstance().saveModel(document); // error, Document is not IAnswer
    

    好吧,希望能帮上忙;祝你好运!

    Playground Link to code

    推荐文章