如何在TypeScript中添加编译时?

时间:2018-08-04 15:47:16

标签: typescript type-level-computation

如何在TypeScript中添加类型系统?

我可以得到后继者(加1)和前任者(减1),但是还没有弄清楚如何获得所需的递归以便通常将两个数字相加:

type Z = 0                           // zero
type S<T> = { item: T }              // successor
type P<T extends S<any>> = T['item'] // predecessor
type N = S<any> | Z                  // number, not exactly right because S<boolean> isn't a number

const zero = 0
const one: S<Z> = { item: 0 }
const two: S<S<Z>> = { item: { item: 0} }
const three: S<S<S<Z>>> = { item: { item: { item: 0 } } }

const predPred3: P<P<typeof three>> = one; // OK

我遇到的问题是递归类型定义:

// Error: Type alias 'Plus' circularly references itself
type Plus<T1, T2> = T2 extends Z ? T1 : Plus<T1, P<T2>>

我尝试使用ReturnType来解决问题,但是语法甚至不正确:

function plus<T1 extends N, T2 extends N>(t1: T1, t2: T2) : T2 extends Z ? T1 : ReturnType<typeof plus<T1, P<T2>>> {
    return t2 === 0? t1 : plus({item: t1}, (t2 as S<any>).item);
}

有没有办法在类型系统中做加法运算?

1 个答案:

答案 0 :(得分:3)

要避免“类型别名'Plus'循环引用自身”的限制,您必须在对象成员类型中引用递归类型,然后使用[]作为indexed type access operator选择适当的成员类型:

type Plus<T1, T2> = {
    z: T1;
    s: T2 extends S<any> ? S<Plus<T1, P<T2>>> : never 
       // conditional type necessary because compiler can't figure out
       // that T2 always extends S here, "never" branch is never taken
}[T2 extends Z ? 'z' : 's']


const p12: Plus<typeof one, typeof two> = three; // typechecks

不过,请注意this comment

  
    

除非@ahejlsberg这样的人可以告诉我们是否可以期待     这样就可以继续工作

  
     

这很聪明,但绝对可以将事情推向极限   有可能的使用。虽然它可能适用于一些小例子,但可以扩展   太可怕了解决那些深度递归类型会消耗很多   时间和资源,并且将来可能会影响递归   检查器中有多个州长。

     

不要这样做!