子类中的方法和属性无法提示

问题描述 投票:0回答:2

我的诉求是通过构建在不同的环境下使用不同的底层实现,但是我想对上层模块隐藏相应的细节。

我期望的是外层是类的正常使用方式

使用 rollupJs 构建时使用

buildEnv
变量。

class subA {
  protected fn() {
    console.log('subA');  
  }
}
class subB {
  protected fn() {
    console.log('subB');  
  }
}

const buildEnv = 'A';
function changeSubClass() {
   return buildEnv === 'A' ? subA : subB;
}

const BaseClass = changeSubClass();
class MainClass extends BaseClass {
  constructor() {
    supper();
    this.fn(); // <--- An error is reported here
  }
}

// Hope to use it this way:
const clsIns = new MainClass();

一种方法是让 subA 继承 MainClass,但对于外部使用,您需要通过调用 changeSubClass 方法来获取实例:

const buildEnv = 'A';
function createSubClass() {
   const Cls = buildEnv === 'A' ? subA() : subB;
   return new Cls();
}

class subA extends MainClass {
  protected fn() {
    console.log('subA');  
  }
}
class subB extends MainClass {
  protected fn() {
    console.log('subB');  
  }
}

class MainClass implements ISubCls {
  constructor() {
    this.fn();
  }
}

interface ISubCls {
  fn: () => void
}

这种使用方式不是我想要的

const clsIns = createSubClass();
typescript class extends rollupjs
2个回答
0
投票

我认为你没有清楚地思考 OOP 方式。如果我理解正确,您正在尝试选择类的实现以根据程序运行的环境实例化它。

我建议你避免继承以支持组合。检查下面的代码:


import { makeSendMailUseCase } from "./make-email-service";

async function main() {
    const sendMail = await makeSendMailUseCase();
    const result = await sendMail.execute("[email protected]", "[email protected]", "testing my email", "Hello ");

    if (result) console.log(' email was sent ')
    else throw new Error('failed sending mail')
}

main().catch(console.log.bind(console))
  

在上面的代码中,应用程序想要发送一封电子邮件。为此,将实例化一个

SendMailUseCase
类,它将协调此任务。为了发送电子邮件,它需要使用外部服务。该服务将被注入到 useCase 中,为了使事情变得简单明了,工厂模式进入行动以决定将注入哪些服务以供使用。 所以主程序并不关心最后使用的是什么服务。


import { createTestAccount } from "nodemailer";
import { SendEmailUseCase } from "./sendmail-usecase";
import { ProductionEmailService } from "./real-email-service";
import { FakeEmailService } from "./fake-email-service";
import { IEmailService } from "./email-service";

export async function makeSendMailUseCase() {
    const isProduction = process.env.NODE_ENV === 'production';

    let emailService: IEmailService;

    if (isProduction) {
        const testAccount = await createTestAccount();
        emailService = new ProductionEmailService(testAccount)
    } else {
        emailService = new FakeEmailService();
    }

    return new SendEmailUseCase(emailService);
}

makeSendMailUseCase 作为一个工厂提供一个全新的 SendMailUseCase 实例,根据应用程序运行的环境选择

IEmailService
的具体实现。


import { EmailFormat, IEmailService } from "./email-service";

export class SendEmailUseCase {
    constructor(private emailProvider: IEmailService) { }

    execute(from: string, to: string, subject: string, content: string): Promise<boolean> {
        const params = {
            from, to, subject, content, format: EmailFormat.Text
        }

        return this.emailProvider.sendEmail(params);
    }
}


SendEmailUseCase
只是将发送电子邮件的任务委托给构造函数中提供的任何具体实现。


export enum EmailFormat {
    Text,
    HTML
}

export type EmailOptions = {
    from: string;
    to: string;
    subject: string;
    content: string;
    format: EmailFormat
}

export interface IEmailService {
    sendEmail(options: EmailOptions): Promise<boolean>;
}

这里是

FakeEmailService
实施:


import { EmailOptions, IEmailService } from "./email-service";

export class FakeEmailService implements IEmailService {
    async sendEmail(options: EmailOptions): Promise<boolean> {
        console.log(options)
        return Promise.resolve(true);
    }
}

这个实现只是满足合约,什么都不做,但它有效。


import { TestAccount, Transporter, createTransport } from "nodemailer";
import { EmailFormat, EmailOptions, IEmailService } from "./email-service";

export class ProductionEmailService implements IEmailService {
    private readonly transporter: Transporter

    constructor(account: TestAccount) {
        this.transporter = createTransport({
            host: account.smtp.host,
            port: account.smtp.port,
            secure: account.smtp.secure,
            auth: {
                user: account.user, // generated ethereal user
                pass: account.pass, // generated ethereal password
            },
        });
    }

    async sendEmail(options: EmailOptions): Promise<boolean> {

        const info = await this.transporter.sendMail({
            from: options.from,
            to: options.to,
            subject: options.subject,
            text: options.format === EmailFormat.Text ? options.content : "",
            html: options.format === EmailFormat.HTML ? options.content : "",
        });

        return info;
    }
}

真正的生产电子邮件服务依赖于另一个图书馆,

nodemailer
最终交付电子邮件。

就是这样!


0
投票
// Import
// prettier-ignore
interface Logger { info: (msg: string) => void }
// prettier-ignore
class ConsoleLogger implements Logger { info(msg: string): void { console.log("[Console]:", msg) } }
// prettier-ignore
class PinoLogger    implements Logger { info(msg: string): void { console.log("[Pino]:"   , msg) } }

// Part 1: Business Entities
interface UserData {
  name: string
}

class AuthService {
  async getUserData(): Promise<UserData> {
    return { name: "Big Lebowski" }
  }
}

class User {
  constructor(private data: UserData) {}
  name = () => this.data.name
}

class PaymentService {
  constructor(private readonly logger: Logger, private readonly user: User) {}
  sendMoney() {
    this.logger.info(`Sending monery to the: ${this.user.name()} `)
    return true
  }
}

// Step 2: Manual DI / composition root
export async function runMyApp() {
  const logger =
    process.env.NODE_ENV === "production"
      ? new PinoLogger()
      : new ConsoleLogger()

  const auth = new AuthService()
  const user = new User(await auth.getUserData())

  const paymentService = new PaymentService(logger, user)
  paymentService.sendMoney()
}

console.log(" ---- My App START \n\n")
runMyApp().then(() => {
  console.log("\n\n ---- My App END")
})
© www.soinside.com 2019 - 2024. All rights reserved.