[英]Objects composed of generic types in TypeScript (type recursion)
Given the following untyped TS:给定以下无类型的 TS:
const compose = (thunk: any): any => {
const res = { ...thunk() };
return { ...res, then: (f: any): any => compose(() => ({...res, ...f()})) };
};
We can use it to make composable objects:我们可以使用它来制作可组合的对象:
const { foo, bar } = compose(() => ({foo: 1})).then(() => ({bar: 2}))
// foo: 1, bar: 2
But typing this in TS seems tricky, as the types are recursive.但是在 TS 中输入这个似乎很棘手,因为类型是递归的。
The best I could come up with is:我能想到的最好的是:
type Compose<T> = (thunk: () => T) => T & { then: Compose<any> };
const compose2 = <T extends {}>(thunk: () => T): ReturnType<Compose<T>> => {
const res = { ...thunk() };
return { ...res, then: (f) => compose2(() => ({ ...res, ...f() })) };
};
This means that all the objects that fall out of compose2
are of type any
.这意味着compose2
中的所有对象都是any
类型。 The end result I'd like to accomplish is something that's typed with all of the composed objects:我想要完成的最终结果是使用所有组合对象键入的内容:
const combined = compose(() => ({foo: 1})).then(() => ({bar: 2}))
// type of combined: { foo: number } & { bar: number } & { then: … }
The way I see it we'd need some sort of Fix
type that can tie the recursive knot, as the type for then
in Compose
needs to recurse.在我看来,我们需要某种可以绑定递归结的Fix
类型,因为Compose
中的then
类型需要递归。
Of course, there might be a way to do it if we inverted the signature of compose
and somehow used CPS.当然,如果我们反转compose
的签名并以某种方式使用 CPS,可能会有办法做到这一点。 I'm open for suggestions!我愿意接受建议!
Note that a 2-ary compose
, let's call it combine
, is no issue:请注意,二元compose
,我们称之为combine
,没有问题:
const combine = <A extends {}, B extends {}>(a: () => A, b: () => B): A & B => ({...a(), ...b()});
const bar = combine(() => ({foo: 1}), () => combine(() => ({bar: 2}), () => ({baz: 3})) )
But it isn't particularly nice to write the statements, so I hoped to pass a closure from the resulting object so I wouldn't have to nest repeated function calls.但是编写语句并不是特别好,所以我希望从生成的 object 传递一个闭包,这样我就不必嵌套重复的 function 调用。
I think you might be looking for this:我想你可能正在寻找这个:
type Compose<O extends object = {}> =
<T extends object>(thunk: () => T) => T & O & {
then: Compose<T & O>
}
const compose: Compose = (thunk) => {
const res = { ...thunk() };
return { ...res, then: f => compose(() => ({ ...res, ...f() })) };
};
The return type of then()
carries some state information you need to represent in your compose type. then()
的返回类型带有一些您需要在 compose 类型中表示的 state 信息。 If we think of O
as "the current state object", then Compose<O>
is a generic function which takes a thunk
of type () => T
for any other object type T
, and returns a T & O & {then: Compose<T & O>}
... that is, the new object is the intersection of T
and O
, and its then()
method has T & O
as the new state.如果我们将O
视为“当前的 state 对象”,那么Compose<O>
() => T
一个通用的thunk
,它对任何其他T
T & O & {then: Compose<T & O>}
6ZF8911C4 T & O & {then: Compose<T & O>}
... 即新的 object 是T
和O
的交集,其then()
方法的T & O
为新的 state。
The fact that the implementation of compose()
type checks is a good sign. compose()
类型检查的实现是一个好兆头。 Let's verify that the compiler understands how calls work:让我们验证编译器是否理解调用的工作原理:
const { foo, bar } = compose(() => ({ foo: 1 })).then(() => ({ bar: "hello" }));
console.log(foo.toFixed(2)) // 1.00
console.log(bar.toUpperCase()); // HELLO
Looks good!看起来不错!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.