在类似的递归类型上运行的键入函数

问题描述

在下面的Typescript代码中,我有两种结构上相似的类型:Nat(可以是ZeroSucc<Nat>,以及Letter,可以是ASucc<Letter>,其中Succ是简单的容器类型。

type Zero = 0;
type A = 'A';

class Succ<T> {
  constructor(public pred: T){}
}

type Nat= Zero | Succ<Nat>

type Letter = A | Succ<Letter>

function next(x: ?): ?{
  return new Succ(x);
}

我的问题是,如何键入next函数,该函数基本上只是包装在函数调用中的Succ的构造函数?本质上是声明,如果您通过Nat,则返回Nat,如果您通过Letter,则返回Letter

我尝试过:

type Countable = Nat | Letter
function next<T extends Countable>(x: T): T { ...

我得到Type 'Succ<T>' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Succ<T>'.

我也尝试直接将其重载:

function next(x: Nat): Nat;
function next(x: Letter): Letter {
  return new Succ(x);
}

但这会返回一个错误,表明签名与实现不兼容。

我尝试直接投射它:

function next<T extends Countable>(x: T): T {
  return new Succ(x) as T;
}

返回错误Conversion of type 'Succ<T>' to type 'T' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional,convert the expression to 'unknown' first. 'T' could be instantiated with an arbitrary type which could be unrelated to 'Succ<T>'.

所以我做到了:return new Succ(x) as unknown as T;

可行,但是感觉很脏(感觉像是铸造到空隙和背面)。作为健全性检查,const x: Letter = next(0);会正确抛出类型错误,Type '0' is not assignable to type 'Letter'.

这是怎么回事?我在写轨道上吗?有更优雅的解决方案吗?

上下文:

我正在使用功能编程语言的实现中的lambda演算解释器。有几个不同的AST-核心lambda演算,核心lambda演算+ let表达式,核心lambda演算+ let +模式匹配等。目前,我的设置方式就是每个构造都是一个类,而递归构造将AST的类型作为参数,因此我们有VariableAbstraction<AST>Application<AST>Let<AST>等,然后将每种不同类型的AST表示为联合,所以

type OrdinaryLambdaCalculus = 
  | Variable 
  | Abstraction<OrdinaryLambdaCalculus> 
  | Application<OrdinaryLambdaCalculus>;

type AugmentedLambdaCalculus = 
  | Variable 
  | Abstraction<AugmentedLambdaCalculus> 
  | Application<AugmentedLambdaCalculus>
  | Let<AugmentedLambdaCalculus>

到目前为止,它的运行情况还不错,但是我希望能够编写一个函数,该函数接受一个或多个AST表达式,并通过添加结构来构造相同类型的新AST,例如:

function etaAbstraction<T extends AST>(ast: T): T {
  let newVar = findFree(ast);
  return new Abstraction(newVar,new Application(ast,newVar));
}

这就是我遇到的麻烦,无法正确输入类型。

解决方法

原因如下:

function next<T extends Countable>(x: T): T {
  return new Succ(x)
}

不起作用的是,如果x(因此T)的类型为ZeroA,则返回值必须相同。但是Succ<Whatever>无法分配给ZeroA

但是,似乎next永远不会返回ZeroA,只能返回Succ的一个实例。因此,如果您这样输入,它将按您期望的那样工作。

type Countable = Nat | Letter

function next<T extends Countable>(x: T): Succ<T> {
  return new Succ(x);
}

const a = next(0) // Succ<0>
const b = next(a) // Succ<Succ<0>>

Playground

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...