gpt4 book ai didi

Typescript 接口(interface)结合对象属性上的映射键

转载 作者:行者123 更新时间:2023-12-03 20:54:10 25 4
gpt4 key购买 nike

我试图通过创建处理程序来创建一种方法,这些处理程序都共享相同的基本定义,但具有特定于每种用途的附加属性。

我有一个路由器、一个上下文和路由处理函数,它们接受上下文作为参数。我希望能够根据路由定义将其他属性附加到上下文中。

如果路由定义是 /hello/:user ,有一个参数user当被称为 HTTP GET /hello/ben那就是所述参数的值。

因此,我们有 context.params = {user: 'ben'}在这种特殊情况下。

我还为这些参数注册了查找函数。

IE。 context.bindings['user] = (param) => User.find(param)
当我注册路由时,我希望处理程序包含上下文和由查找函数解析的任何附加键。例如

// In this example, each GET route has a :parameter and a binding for it so their
// handler should receive the result of the binding function as a key on context
// Both receive params because that's just part of context always.

router.binding('user', (user) => User.find(user));
router.binding('photo', (photo) => Photo.find(photo));
router.get("/bar/:photo", ({ params, photo }: HttpContextContract) => {}) // 1
router.get("/bar/:user", ({ params, user }: HttpContext) => {}) // 2

// in (1), the interface HttpContextContract doesn't know about photo so it moans
// in (2), the class HttpContext just lets any property go and there's no helpful typing/intellisense etc

我不想每次都为每条路由定义一个新接口(interface) HttpContextContract & { user: User }例如。我相信我应该能够使用 type Handlers = Record<keyof typeof handlers, {}> 的内容。但我似乎无法让它工作。

我已经将上述部分的基本示例放在一起并将其放入 Typescript playground所以希望更容易大致了解我想要实现的目标
type Binding = (param: string) => any;
type RouteHandler = (ctx: HttpContextContract) => any;

interface RouteBindings {
[key: string]: Binding;
}

interface RouteHandlers {
[key: string]: RouteHandler;
}

interface HttpContextContract {
params: any;
}

interface HttpContext {
[key: string]: any; // this allows me to attach the new keys to the instance
}

class HttpContext implements HttpContextContract {
public params: any = {};
}

class Router {
public bindings: RouteBindings = {};
public handlers: RouteHandlers = {};

public binding(key: string, binding: Binding) {
this.bindings[key] = binding;
}

public get(path: string, handler: any) {
this.handlers[path] = handler;
}

public find(path: string): RouteHandler {
return this.handlers[path];
}
}

class Server {
constructor(protected router: Router) {}

getParams(path: string) {
const matches = path.match(/:([^/]+)/gi) || [];
return matches.map(s => s.substring(1));
}

handle(path: string) {
const ctx = new HttpContext(); // as HttpContext & { 'foo2': 'bar '}

this.getParams(path).forEach((param: string) => {
const binding = this.router.bindings[param];
if (binding) {
// Object.defineProperty(ctx, param, {
// get: binding
// });
ctx[param] = binding(param);
}
});

const handler = this.router.find(path);
return handler(ctx);
}
}

const router = new Router();
const server = new Server(router);

class Photo {
constructor(public name: string) {}
}

router.binding("user", () => "BOUND USER STRING");
router.binding("photo", () => new Photo("test"));

// This has no idea about the user property, even though it's there and readable
router.get("/foo/:user", ({ user }: HttpContextContract) => {
return `"${user}" <- from bindings`;
});

// This now lets me use photo, but doesn't tell me user doesn't exist there
router.get("/bar/:photo", ({ photo, user }: HttpContext) => {
return `"${JSON.stringify(photo, null, 2)} ${user}" <- from bindings`;
});

const out1 = server.handle("/foo/:user");
const out2 = server.handle("/bar/:photo");

console.log(out1);
console.log(out2);

// type ExtendedProperties<T> = { [P in keyof T]: T[P] };
// & ExtendedProperties<Record<keyof typeof this.router, {}>>;
// type BB = Router['handlers']
// type Handlers = Record<keyof typeof handlers, {}>

最佳答案

我认为你当前的 API 有点过于动态,TypeScript 无法处理。但这里有一些可能会有所帮助的东西。

class Router<T extends Record<string, () => any> = {}> {
bindings: T;

constructor(handlers: T) {
this.bindings = handlers;
}

bind<Key extends string, Handler>(key: Key, handler: Handler) {
return new Router<{ [key in Key]: Handler} & T>({
...this.bindings,
...({ [key]: handler } as { [key in Key]: Handler })
});
}
}
function createRouter() {
return new Router({});
}

const router = createRouter()
.bind('hello', () => "world")
.bind('foo', () => Math.random());
const handler = router.bindings.hello; // const helloHandler: () => "world"
const fooHandler = router.bindings.foo; // const fooHandler: () => number


只要您在调用 .bind() 时使用字符串文字,您将保留回调的类型。

编辑:这是一个经过调整的版本,希望您可以从中构建所需的内容。

// builds typed objects one property at a time
// ex. const obj = new Builder({}).add("hello", "world").build();
class Builder<T> {
readonly obj: T;
constructor(value: T) {
this.obj = value;
}
add<K extends string, V>(key: K, value: V) {
let next = {...this.obj, ...({[key]: value} as { [key in K]: V })};
return new Builder(next);
}
build(): T { // finish building the object
return this.obj;
}
}

class Route<T> {
bindings: T;
path: string;

constructor(path: string, bindings: (builder: Builder<{}>) => Builder<T>) {
this.path = path;
this.bindings = bindings(new Builder({})).build();
}
}

let route = new Route("/api", (bindings) => bindings
.add("hello", "world")
.add("person", () => Math.random())
);

关于Typescript 接口(interface)结合对象属性上的映射键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61440762/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com