I'm currently writing a parser. The parser produces an AST, and then I walk on it using various passes. The AST is (simplified):

```js
type LiteralExpr = {
  readonly kind: 'literal',
  readonly value: number,
};
type UnaryExpr = {
  readonly kind: 'unary',
  readonly operator: '!' | '-',
  readonly operand: Expr,
};
type BinaryExpr = {
  readonly kind: 'binary',
  readonly left: Expr,
  readonly operator: '+' | '-' | '*' | '/',
  readonly right: Expr,
};
/** Parenthesized expression */
type GroupingExpr = {
  readonly kind: 'grouping',
  readonly subExpr: Expr,
};
type Expr = LiteralExpr | UnaryExpr | BinaryExpr | GroupingExpr;

```

Each pass changes the AST a little, producing a new AST. For example, I have a pass the eliminates the `grouping` nodes:

```js
class ParensRemover {
  doPass(expr: Expr): Expr {
    switch (expr.kind) {
      case 'literal': return expr;
      case 'unary': return { ...expr, operand: this.doPass(expr.operand) };
      case 'binary': return { ...expr, left: this.doPass(expr.left), right: this.doPass(expr.right) };
      case 'grouping': return this.doPass(expr.subExpr);
    }
  }
}

```

However, this code becomes boilerplate-y quickly, esp. when I have tons of nodes, and so I would like to refactor it to a base recursive class using the visitor pattern:

```js
abstract class ASTVisitor {
  doPass(expr: Expr): Expr {
    switch (expr.kind) {
      case 'literal': return this.visitLiteral(expr);
      case 'unary': return this.visitUnary(expr);
      case 'binary': return this.visitBinary(expr);
      case 'grouping': return this.visitGrouping(expr);
    }
  }

  protected visitLiteral(expr: LiteralExpr): Expr {
    return expr;
  }
  protected visitUnary(expr: UnaryExpr): Expr {
    return { ...expr, operand: this.doPass(expr.operand) };
  }
  protected visitBinary(expr: BinaryExpr): Expr {
    return { ...expr, left: this.doPass(expr.left), right: this.doPass(expr.right) };
  }
  protected visitGrouping(expr: GroupingExpr): Expr {
    return { ...expr, subExpr: this.doPass(expr.subExpr) };
  }
}

class ParensRemover extends ASTVisitor {
  protected visitGrouping(expr: GroupingExpr): Expr {
    return this.doPass(expr.subExpr);
  }
}

```

So far so good. The problem with this code is that the next passes after `ParensRemover` will have to deal with the node kind `grouping`, although of course there won't be nodes of this kind. This may not seems a like big deal, but I have many kinds of nodes, and many passes, almost each of one changes the AST slightly - removes nodes or add another, or change the type of a property. So I changed the AST `Expr` type to the following:

```js
type LiteralExpr = {
  readonly kind: 'literal',
  readonly value: number,
};
type UnaryExpr<Addition> = {
  readonly kind: 'unary',
  readonly operator: '!' | '-',
  readonly operand: ExprBase<Addition>,
};
type BinaryExpr<Addition> = {
  readonly kind: 'binary',
  readonly left: ExprBase<Addition>,
  readonly operator: '+' | '-' | '*' | '/',
  readonly right: ExprBase<Addition>,
};
/** Parenthesized expression */
type GroupingExpr = {
  readonly kind: 'grouping',
  readonly subExpr: BeforeRemoveParensExpr,
};
type ExprBase<Addition> = LiteralExpr | UnaryExpr | BinaryExpr | Addition;
type BeforeRemoveParensExpr = ExprBase<GroupingExpr>;
type AfterRemoveParensExpr = ExprBase<never>;

```

But now how will the `ASTVisitor` know the right type? I tried the following:

```js
type AllExprs = BeforeRemoveParensExpr | AfterRemoveParensExpr;

type PickExpr<E extends AllExprs, K extends E['kind']> = /* details not important, this type pulls a specific kind out of Expr */;

abstract class ASTVisitor<InputExpr extends AllExprs, OutputExpr extends AllExprs> {
  doPass(expr: InputExpr): OutputExpr {
    switch (expr.kind) {
      case 'literal': return this.visitLiteral(expr as any);
      case 'unary': return this.visitUnary(expr as any);
      case 'binary': return this.visitBinary(expr as any);
      case 'grouping': return this.visitGrouping(expr as any);
    }
  }

  protected visitLiteral(expr: PickExpr<InputExpr, 'literal'>) {
    return expr as unknown OutputExpr;
  }
  protected visitUnary(expr: PickExpr<InputExpr, 'unary'>) {
    return { ...expr, operand: this.doPass(expr.operand) } as unknown as OutputExpr;
  }
  protected visitBinary(expr: PickExpr<InputExpr, 'binary'>) {
    return { ...expr, left: this.doPass(expr.left), right: this.doPass(expr.right) } as unknown as OutputExpr;
  }
  protected visitGrouping(expr: PickExpr<InputExpr, 'grouping'>) {
    return { ...expr, subExpr: this.doPass(expr.subExpr) } as unknown as OutputExpr;
  }
}

class ParensRemover extends ASTVisitor<BeforeRemoveParensExpr, AfterRemoveParensExpr> {
  protected visitGrouping(expr: GroupingExpr): AfterRemoveParensExpr {
    return this.doPass(expr.subExpr);
  }
}

```

But I'm not satisfied with this solution. Except the many casts to `any` in `ASTVisitor`, it loses type safety. If I forget to override a `visitX()` for `X` that should change between passes, I will not get compiler errors and instead the program will fail somewhere in a bizarre way.

Can I do what I want *without* losing the safety that TypeScript provides? I can change the representation of the AST to something else, if required.

Sorry for the lengthy post. Thanks in advance.