除了this question和this answer之外,我目前首选的处理带有太多参数的函数的方法是使用context(对象文字作为命名参数)。然而,当使用打字稿时,一切又变得复杂起来。 根据这篇博客,我实现的构建器模式:
class Builder<T extends Record<string, unknown> = {}> {
private constructor(private readonly object: T) {}
public add<Obj extends object>(object: Obj): Builder<T & Obj>;
public add<K extends string, V>(key: K, value: V): Builder<T & { [k in K]: V }>;
public add(arg1: any, arg2?: any) {
if (typeof arg1 === 'string') {
return new Builder(Object.assign(this.object, { [arg1]: arg2 }));
}
return new Builder(Object.assign(this.object, arg1));
}
public build(): T {
return this.object;
}
static create<T extends Record<string, unknown> = {}>(object: T = {} as any): Builder<T> {
return new Builder(object);
}
}
用例:
type InferObject<T extends Builder> = T extends Builder<infer Res> ? Res : never;
export type AddField<T extends Builder, U extends object> = Builder<InferObject<T> & U>;
type BaseContext = Builder<{
a: number;
b: number;
}>;
// Here I need to pre-define the `SecondContext` type to fulfil the `second` function signature
type SecondContext = AddField<BaseContext, {
c: number;
}>;
// Here I need to pre-define the `ThirdContext` type to fulfil the `third` function signature
type ThirdContext = AddField<SecondContext, {
d: number
}>;
first(Builder.create({ a: 1, b: 2 }));
function first(context: BaseContext) {
const { a, b } = context.build();
// do sth around a, b
const newContext = context.add({ c: 3 });
second(newContext);
}
function second(context: SecondContext) {
const { a, b, c } = context.build();
// do sth around a, b, c
const newContext = context.add({ d: 4 });
third(newContext);
}
function third(context: ThirdContext) {
const { a, b, c, d } = context.build();
// do sth around a, b, c, d
}
这就是大惊小怪的地方,我必须预先定义所有的上下文类型来完成函数的签名。使用 javascript 时,我可以毫不费力地将我的新上下文传递给以下函数。
我想知道我是否滥用了构建器模式或者这里有什么解决方法吗? TS游乐场
[已更新]
我同意我的
Builder
实现对于这种情况似乎有点矫枉过正,并且 first
、second
、third
功能令人困惑,这是一个简化且更现实的示例(游乐场链接)。
type BaseParam = {
a: number;
b: number;
};
type SecondParam = BaseParam & { c: number };
type ThirdParam = SecondParam & { d: number };
const someUrl = "";
first({ a: 1, b: 2 });
async function first(firstParam: BaseParam) {
const { a, b } = firstParam;
// do some calculation around a, b
const c = await fetch(someUrl, /** using a, b calc result */).then(res => res.json());
// addtional logic for c
second({ a, b, c });
}
async function second(secondParam: SecondParam) {
const { a, b, c } = secondParam;
// do some calculation around a, b, c
const d = await fetch(someUrl, /** using a, b, c calc result */).then(res => res.json());
// addtional logic for d
third({ a, b, c, d });
}
async function third(thirdParam: ThirdParam) {
const { a, b, c, d } = thirdParam;
// do some calculation around a, b, c, d
const e = await fetch(someUrl, /** using a, b, c, d calc result */).then(res => res.json());
// // addtional logic for e
// then maybe fourth, fifth and so on...
}
老实说,我对这个解决方案也不满意,这对我来说似乎是一个设计问题,但我找不到更好的解决方案。或者我可能需要将问题标题更改为“如何重构丑陋的嵌套函数”?