使用接口的 Typescript 依赖倒置

Typescript dependency invertion using interfaces

我在 TypeScript 中有一个 class 叫做 UsersUseCases:

import UsersRepository from './UsersRepository';

class UsersUseCases{
    private userRepository:IUsersRepository;

    constructor(receivedUsersRepository:any = null){
        this.userRepository =
            receivedUsersRepository === null ? new UsersRepository() : new receivedUsersRepository();
   }

   /** other methods **/
}

我的目标是在 userRepository 上使用 依赖注入 ,同时定义要使用的默认存储库。这样,当 运行 代码真实时,我不需要传递 receivedUsersRepository,而只需要在我的测试中传递。有更好的方法吗?

IUsersRepository代码:

export interface IUsersRepository {
    createUser(email: string, password: string): Promise<any>;
}

UsersRepository代码:

class UsersRepository implements IUsersRepository {  
    async createUser(email: string, password: string) {
        /** Implementation **/
    }
}

export default UsersRepository;

我试过以下代码:

import UsersRepository from './UsersRepository';

class UsersUseCases{
    private userRepository:IUsersRepository;

    constructor(ReceivedUsersRepository: IUsersRepository | null = null) {
        this.userRepository =
        ReceivedUsersRepository === null ? new UsersRepository() : new ReceivedUsersRepository();
    }

   /** other methods **/
}

但是我遇到了以下错误:

This expression is not constructable. Type 'IUsersRepository' has no construct signatures.ts(2351)

对不起我的英语。我不是母语人士。

如果问题是关于如何解决您的错误(而不是主要关于依赖注入),那么您应该了解 construct signaturesreceivedUsersRepository 参数要么是 null,要么是生成 IUsersRepository 实例的无参数构造函数。它本身不是 IUsersRepository 实例。所以你应该这样写而不是IUsersRepository | null

receivedUsersRepository: (new () => IUsersRepository) | null

然后您的代码将按预期运行:

constructor(receivedUsersRepository: (new () => IUsersRepository) | null) {
    this.userRepository =
        receivedUsersRepository === null ?
            new UsersRepository() : new receivedUsersRepository(); // okay
}

万岁。


当然,dependency injection意味着你总是在传递IUsersRepository,即使当运行代码是真实的。这是一个外部服务。而且您绝对不会 在 class 中构建 该服务。所以做“真正的”依赖注入看起来更像

class UsersUseCases {
    constructor(private userRepositoryService: IUsersRepository) { }
    /** other methods **/
}

然后你的测试代码可能看起来像

const mockUsersRepository = {
    async createUser(email: string, password: string) {
        console.log("I'M NOT REALLY DOING IT");
    }
}
const testUsersUseCases = new UsersUseCases(mockUsersRepository);

而您的真实代码看起来像

const userRepo = new UsersRepository();
const usersUseCases = new UsersUseCases(userRepo);

Playground link to code