Javascript 设计模式-编写API包装库(typescript)

Javascript 设计模式-编写API包装库(typescript),javascript,typescript,class,inheritance,composition,Javascript,Typescript,Class,Inheritance,Composition,在试图想出一种编码方法时遇到了一点问题。让我先说这不是我的专长,我以前也没有尝试过这样的事情 目标:为客户端创建包装器API库。它应该公开一种方法/类,用一个basePath实例化lib,然后公开带名称空间的对象/类,这些对象/类上有调用内部API的方法,因此为了简单起见,使用者只需要传入每个API调用可能需要的任何数据,这样我们就可以一般地处理错误之类的事情 我知道我在这里所做的不会起作用,但这主要是为了了解我希望实现的目标 我的问题是 是否应该从静态方法设置basePath,而basePa

在试图想出一种编码方法时遇到了一点问题。让我先说这不是我的专长,我以前也没有尝试过这样的事情

目标:为客户端创建包装器API库。它应该公开一种方法/类,用一个basePath实例化lib,然后公开带名称空间的对象/类,这些对象/类上有调用内部API的方法,因此为了简单起见,使用者只需要传入每个API调用可能需要的任何数据,这样我们就可以一般地处理错误之类的事情

我知道我在这里所做的不会起作用,但这主要是为了了解我希望实现的目标

我的问题是

  • 是否应该从静态方法设置basePath,而basePath只是一个属性
  • 还可以如何组合它,这样我们就可以共享方法和对basePath的访问,而不需要每次都实例化它
我感谢所有的帮助和建议

abstract class Base { 
    constructor(private basePath: string = '') {}
    async get<T>(endpoint: string, config?: RequestInit | undefined): Promise<T> { 
        const response = await fetch(this.basePath + endpoint, config);
        return response.json();
    }
    // ... other methods that would be available  
}

// Exported to the consumer for ease of use. Others could be `Comments.postNewComment({user, data})`
class Posts extends Base {
  getAllPosts() {
    return this.get<PostsData>('/posts')
  }
}

// In the main index file for lib, it would export all the classes with available API methods (such as Posts, Auth, Comments etc) that the consumer would be able to use.
// Issue comes up when creating an instance of these because it would require the basePath again
export { Posts: new Posts(), Comments: new Comments() }; 

// Ideal usage: Main file of the app:
import LibName from 'lib-name'
new LibName({ basePath: 'api.whatever' }) // Left out re: brevity, but would be the class where we instantiate the basePath, and anything else

// In other locations through the app, be able to call the various methods from different sections (Auth.login, Comments.addNewComment etc)
import { Posts } from 'lib-name';
Posts.getAllPosts();
抽象类基类{
构造函数(私有基路径:字符串=“”){}
异步获取(端点:字符串,配置?:RequestInit |未定义):承诺{
const response=wait fetch(this.basePath+endpoint,config);
返回response.json();
}
//…其他可用的方法
}
//出口到消费者,以便于使用。其他可以是` Comments.postnocomment({user,data})`
班级职位扩大基数{
getAllPosts(){
返回此。get(“/posts”)
}
}
//在lib的主索引文件中,它将导出消费者能够使用的所有具有可用API方法(如Posts、Auth、Comments等)的类。
//创建这些实例时会出现问题,因为它将再次需要basePath
导出{Posts:newposts(),Comments:newcomments()};
//理想用法:应用程序的主文件:
从“lib name”导入LibName
new LibName({basePath:'api.whatever'})//省略了re:breeficity,但将是我们实例化basePath和其他任何内容的类
//在应用程序的其他位置,能够从不同部分调用各种方法(Auth.login、Comments.addNewComment等)
从“lib name”导入{Posts};
Posts.getAllPosts();

我开始是这样做的,但最后一个示例公开了一个导出(DevToClient.X)下的所有方法,而我希望将它们的名称空间从相应的父对象中移除。

为什么不根据对象的类型为
端点
创建一个可实例化的类呢

class Endpoint<T extends {id: string}, Creatable = T> { 
    constructor(private endpointPath: string) {}
    async get(id: string, config?: RequestInit | undefined): Promise<T> { 
        const response = await fetch(this.endpointPath + '/' + id, config);
        return response.json();
    }
    // stubs of sample methods
    async getAll( someFilters?: any ): Promise<T[]> {}
    async update(changes: Partial<T> & {id: string}): Promise<T> {}
    async createNew( object: Creatable ): Promise<T> {}
}
您可以这样使用它:

import {Posts} from 'some-lib'

const post = Posts.get("321"); // Promise<PostObject>

// the arguments depend on your partuicular api structure
const latest = Posts.getAll({limit: 10, sort: 'date'}); // Promise<PostObject[]>

const updated = Posts.update({id: "123", title: "New Title"}); // Promise<PostObject>
从'some lib'导入{Posts}
const post=Posts.get(“321”);//允诺
//参数取决于部分api结构
const latest=Posts.getAll({limit:10,sort:'date'});//允诺
const updated=Posts.update({id:“123”,title:“New title”});//允诺

如果您想在验证等方面支持端点之间不同的更高级配置,那么您可以使用组合。
端点
可以接受构造函数中某种类型的
配置
对象或类。

否,不应使用单例。如果希望它们具有名称空间,请使用类似于
const lib=new lib({basePath:…})的结构;const posts=lib.posts();posts.getAll()
Posts
类可能不应该继承自
Base
,而应该使用composition,而
Base.prototype.Posts
方法应该通过当前端点对其进行实例化。@Bergi当您说Posts类应该使用composition时,您是否可以解释或删除一个如何实现这一点的示例,我正在与心智模型作斗争,但同意你的观点。我的意思是你会有一个
类端点{constructor(basePath){…}posts(){return new posts(this)}}
类端点{constructor(Endpoint){…}getAll(){return this.Endpoint.get('/posts')}
@Bergi感谢你的帮助!这真的很接近我想要的。有没有一种方法可以让我们从lib中只公开类的实例化版本,比如posts,这样我们就不需要调用
const lib=new lib({});const post=lib.posts
,因为它将跨越不同的文件,并且理想情况下只从lib本身导入。是的,但这将是糟糕的api设计。您应该能够在同一个应用程序中实例化不同的端点。如果它将在不同的文件中使用,只需从实例化库的模块中导入部件,而不是从库模块中导入部件。
import {Posts} from 'some-lib'

const post = Posts.get("321"); // Promise<PostObject>

// the arguments depend on your partuicular api structure
const latest = Posts.getAll({limit: 10, sort: 'date'}); // Promise<PostObject[]>

const updated = Posts.update({id: "123", title: "New Title"}); // Promise<PostObject>