【问题标题】:Change return values based on replaced extended method within class根据类中替换的扩展方法更改返回值
【发布时间】:2019-02-04 05:17:47
【问题描述】:

我有一个 FileHandler 类,然后是一个扩展原始的 FileOrNullHandler 类。

问题在于,FileOrNullHandler 中从 FileHandler 继承的方法被原始类中创建的返回类型卡住了。

export class FileHandler {
    static async readFileFromFileQuery (fq: FileQuery): Promise<File> {
        const { path, encoding, flag } = FileQueryHandler.make(fq);
        const content = await promisify(fs.readFile)(path, { encoding, flag })
        return { path, encoding, flag, content };
    }
    static async readFile (a: Path | FileQuery, b?: Omit<FileQuery, 'path'>): Promise<File> {
        if (typeof a === 'string') a = FileQueryHandler.getFromPath(a, b);
        return this.readFileFromFileQuery(a);
    }
    static async readFiles (a: (Path | FileQuery)[] | Directory, b?: Omit<FileQuery, 'path'>): Promise<File[]> {        
        if (a instanceof Array) return Promise.all(a.map(p => this.readFile(p, b)));
        return this.readFiles(PathHandler.getFromDirectory(a), b);
    }
    static async readFilesFromDirectory(a: Path | FileQuery, b?: Omit<FileQuery, 'path'>): Promise<File[]> {
        const ps = await DirectoryHandler.readDirectory(a);    
        if (typeof a === 'string') return await (this).readFiles(ps, b);
        return await this.readFiles(ps, a);
    }
}

export class FileOrNullHandler extends FileHandler {
    static async readFileFromFileQuery (fq: FileQuery): Promise<File | null> {
        return orNull(() => FileHandler.readFileFromFileQuery(fq));
    }
}

我在这里看到了获取正确类型的两个选项之一。

  1. 根据this设置原始方法的相对返回类型。 (可能不可能)
  2. FileOrNullHandler 中覆盖方法ReturnType

【问题讨论】:

    标签: typescript


    【解决方案1】:

    OOP 的一个普遍原则是派生类应该能够替换基类。在这种情况下,派生类不能替换基类,因为派生类有一个返回类型 (null),而基类的客户端不知道该返回类型。

    蜜蜂说我们可以很接近你想要的。

    首先我不会使用只有静态方法的类,我会创建一个带有实例方法的类,并声明该实例类型的const 并将其导出以供人们用作单例。

    其次,我会将通用功能移动到通用基类中,readFileFromFileQuery 方法是抽象的,类是通用的。这允许我们插入返回 File 或返回 File | null 的版本,而不会违反 Typescript(和 OOP)一致性规则

    最终的解决方案看起来像这样(我填写了一些类型,不确定它们的实际定义是什么,但我添加了代码无错误所需的最低限度):

    abstract class _FileHandlerBase<T> {
      abstract readFileFromFileQuery(fq: FileQuery): Promise<T>;
      async readFile(a: Path | FileQuery, b?: Omit<FileQuery, 'path'>): Promise<T> {
        if (typeof a === 'string') a = FileQueryHandler.getFromPath(a, b);
        return this.readFileFromFileQuery(a);
      }
      async readFiles(a: (Path | FileQuery)[] | Directory, b?: Omit<FileQuery, 'path'>): Promise<T[]> {
        if (a instanceof Array) return Promise.all(a.map(p => this.readFile(p, b)));
        return this.readFiles(PathHandler.getFromDirectory(a), b);
      }
      async readFilesFromDirectory(a: Path | FileQuery, b?: Omit<FileQuery, 'path'>): Promise<T[]> {
        const ps = await DirectoryHandler.readDirectory(a);
        if (typeof a === 'string') return await (this).readFiles(ps, b);
        return await this.readFiles(ps, a);
      }
    }
    export class _FileHandler extends _FileHandlerBase<File> {
      async readFileFromFileQuery(fq: FileQuery): Promise<File> {
        const { path, encoding, flag } = FileQueryHandler.make(fq);
        const content = await promisify(fs.readFile)(path, { encoding, flag })
        return { path, encoding, flag, content };
      }
    }
    export const FileHandler = new _FileHandler();
    
    export class _FileOrNullHandler extends _FileHandlerBase<File | null> {
      async readFileFromFileQuery(fq: FileQuery): Promise<File | null> {
        return orNull(() => FileHandler.readFileFromFileQuery(fq));
      }
    }
    
    export const FileOrNullHandler = new _FileOrNullHandler();
    
    FileHandler.readFileFromFileQuery(null!) // Promise<File>
    FileOrNullHandler.readFileFromFileQuery(null!) // Promise<File | null>
    
    FileHandler.readFiles(null!) // Promise<File[]>
    FileOrNullHandler.readFiles(null!) // Promise<(File | null)[]>
    
    // Some assumptions
    type Path = string;
    
    interface FileQuery {
      path: string, flag?: string, encoding?: string | null
    }
    export interface File {
      path: string, flag?: string, encoding: string | undefined | null, content: Buffer | string
    }
    interface Directory {
      path: string, isDir: true
    }
    
    declare var FileQueryHandler: {
      make(fq: FileQuery): FileQuery
      getFromPath(s: string, b?: Omit<FileQuery, 'path'>): FileQuery;
    }
    
    declare var DirectoryHandler : {
      readDirectory(a: Path | FileQuery) : FileQuery[]
    }
    type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
    
    function orNull<T>(fn: () => T) {
      try {
        return fn();
      } catch (e) {
        return null;
      }
    }
    declare const PathHandler: {
      getFromDirectory(d: Directory): FileQuery[];
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多