简体   繁体   English

TypeScript中泛型类型组成的对象(类型递归)

[英]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 是TO的交集,其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!看起来不错!

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM