考虑到可以提供对象图“扩展”版本的API,我正在寻找一种简单的方法来定义返回的类型。
以这些定义为例
interface A {
id: string;
q: string;
b: B;
cs: C[];
}
interface B {
id: string;
prev: B;
c: C;
}
interface C {
id: string;
foo: string;
ds: D[];
}
interface D {
id: string;
e: E;
}
interface E {
id: string;
}
我需要导出例如以下类型
type A0 = {
id: string,
q: string,
b: { id: string },
cs: Array<{id: string}>,
};
type A1 = {
id: string,
q: string,
b: {
id: string,
prev: { id: string },
c: { id: string },
},
cs: Array<{
id: string,
foo: string,
ds: Array<{id: string}>,
}>,
};
type A2 = {
id: string,
q: string,
b: {
id: string,
prev: {
id: string,
prev: { id: string },
c: { id: string },
},
c: {
id: string,
foo: string,
ds: Array<{
id: string,
}>,
},
},
cs: Array<{
id: string,
foo: string,
ds: Array<{
id: string,
e: { id: string},
}>,
}>,
};
我所拥有的是以下类型(对于较大级别,依此类推)
export type Retain0Levels<T> = {
[P in keyof T]:
T[P] extends infer TP
? TP extends Primitive
? TP
: TP extends any[]
? Array<{ id: string }>
: { id: string }
: never
};
export type Retain1Level<T> = {
[P in keyof T]:
T[P] extends infer TP
? TP extends Primitive
? TP
: TP extends any[]
? { [I in keyof TP]: Retain0Levels<TP[I]> }
: Retain0Levels<TP>
: never
};
export type Retain2Levels<T> = {
[P in keyof T]:
T[P] extends infer TP
? TP extends Primitive
? TP
: TP extends any[]
? { [I in keyof TP]: Retain1Level<TP[I]> }
: Retain1Level<TP>
: never
};
type Primitive = string | Function | number | boolean | symbol | undefined | null;
这很好用,但是当然有两个问题:
A
)转换为他们请求的类型(通过提供一个参数来指示要扩展多少级)。最好根据提供的输入参数获得返回类型。作为(2)的示例,假设我们有一个调用后端API的函数,例如getAs(levels: number): A[]
。我希望它是getAs(levels: number): Array<RetainLevels<A, levels>>
(或类似名称)。如果不是,则使用者必须使用来调用该方法。 getAs(5) as RetainLevels<A, 5>
。
有人对如何改善我目前要解决的上述问题之一有任何建议吗?
P.S。 @titian-cernicova-dragomir的荣誉 从谁的answer中得出我目前拥有的东西
已纠正@jcalz指出的错误,并根据定义的类型添加了“动态返回类型”用例的示例。
答案 0 :(得分:1)
为了使您的类型定义不那么重复,我建议使用类似的东西:
type PrevNum = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
// lengthen as needed
export type RetainLevel<N extends number, T> = {
[P in keyof T]: T[P] extends infer TP
? TP extends Primitive
? TP
: TP extends any[] ? { [I in keyof TP]: Rec<N, TP[I]> } : Rec<N, TP>
: never
};
type Rec<N extends number, T> = N extends 0
? { id: string }
: RetainLevel<PrevNum[N], T>;
这使您可以指定诸如RetainLevel<10, XXX>
之类的类型,而不是Retain10Level<XXX>
。 TypeScript并不能真正让您执行类型级别的算术运算,因此无法从任意数中减去一个。 PrevNum
元组可能已经足够好了;只要您认为人们想要的最大合理价值,就可以做到。
进行测试以查看是否提供您指定的类型:
type MutuallyExtends<T extends U, U extends V, V = T> = true;
type A0 = RetainLevel<0, A>;
type A0Okay = MutuallyExtends<A0, A0Manual>; // okay
type A1 = RetainLevel<1, A>;
type A1Okay = MutuallyExtends<A1, A1Manual>; // okay
这些看起来还可以。
type A2 = RetainLevel<2, A>;
type A2Okay = MutuallyExtends<A2, A2Manual>; // error?
看起来您的A2
类型与RetainLevel<2, A>
不同。尽管我检查了您的Retain2Levels<A>
与RetainLevel<2, A>
相同,但是我猜您的代码并没有完全构成minimum reproducible example。我认为这没问题。
对于转换部分,请在代码中举一个您要表达的意思的示例,然后也许我或其他人将能够回答。如果您是指将数字作为参数传递,则如果您在该数字的类型N
中使该函数通用,则上述内容可能会有所帮助。似乎是一个单独的问题,但您是老板!
更新:看到getAs()
的示例,是的,既然RetainLevel
的数字为N
,那么您可以将getAs()
设为通用的:
declare function getAs<N extends number>(levels: N): Array<RetainLevel<N, A>>;
这应该按照您想要的方式工作:
const a0s = getAs(0); // Array<RetainLevel<0, A>>, same as Array<A0>
const a1s = getAs(1); // Array<RetainLevel<1, A>>, same as Array<A1>
const a10s = getAs(10); // Array<RetainLevel<10, A>>;
好的,希望能有所帮助;祝你好运!