Scala 片段到 TypeScript(如何转换抽象类型成员)
Scala snippet to TypeScript (how to convert abstract type members)
我在 Scala 中有一小段值级别和类型级别列表
sealed trait RowSet {
type Append[That <: RowSet] <: RowSet
def with[That <: RowSet](that: That): Append[That]
}
object RowSet {
case object Empty extends RowSet {
type Append[That <: RowSet] = That
override def with[That <: RowSet](that: That): Append[That] = that
}
case class Cons[A, B <: RowSet](head: A, tail: B) extends RowSet { self =>
type Append[That <: RowSet] = Cons[A, tail.Append[That]]
override def with[That <: RowSet](that: That): Append[That] = Cons(head, tail ++ that)
}
}
现在,我正在尝试将它转换为 TypeScript。由于我们没有 Abstract Type Members 功能,我似乎无法找到不需要在某些时候键入 cast 的解决方案。
我目前在 TypeScript 中拥有的内容(也可在 Playground 上获得)
abstract class RowSet {
abstract with<That extends RowSet>(that: That): RowSet
}
type Append<This extends RowSet, That extends RowSet> =
This extends Cons<infer A, infer B> ? Cons<A, Append<B, That>> : That;
class Empty extends RowSet {
public with<That extends RowSet>(that: That): That {
return that;
}
}
class Cons<A, B extends RowSet> extends RowSet {
constructor(public readonly head: A, public readonly tail: B) {
super();
}
public with<That extends RowSet>(that: That): Cons<A, Append<B, That>> {
return new Cons(this.head, this.tail.with(that) as Append<B, That>)
}
}
const x = new Cons(5, new Empty) // Cons<number, Empty>
const y = new Cons("hi", new Empty) // Cons<string, Empty>
const z = x.with(y) // Cons<number, Cons<string, Empty>>
我感兴趣的是我们是否可以避免在此处强制转换:
return new Cons(this.head, this.tail.with(that) as Append<B, That>)
似乎 TypeScript 理解该值实际上是 Append<B, That>
因为它不允许转换为任何不同的东西,例如Append<B, B>
或类似的东西。但是因为我们使用 abtract class RowSet
中的 with
我们最终得到 Cons<A, RowSet>
.
我们能否以不同的方式定义 RowSet,以便 TypeScript 在没有我们帮助的情况下正确推断出所有内容?也许有不同的抽象类型成员转换方式(从 Scala 转换时)?
感谢 Oleg Pyzhcov 的评论,我能够在没有任何手动类型转换的情况下使其工作。 F-bounded 多态性被建议作为解决这个问题的一种方法,事实证明它在这里确实有帮助
解决方案看起来像这样,不需要类型转换,一切都按预期工作
abstract class RowSet<T extends RowSet<T>> {
abstract with<That extends RowSet<That>>(that: That): Append<T, That>
}
type Append<This extends RowSet<This>, That extends RowSet<That>> =
This extends Cons<infer A, infer B> ? Cons<A, Append<B, That>> : That;
class Empty extends RowSet<Empty> {
public with<That extends RowSet<That>>(that: That): That {
return that;
}
}
class Cons<A, B extends RowSet<B>> extends RowSet<Cons<A,B>> {
constructor(public readonly head: A, public readonly tail: B) {
super();
}
public with<That extends RowSet<That>>(that: That): Cons<A, Append<B, That>> {
return new Cons(this.head, this.tail.with(that))
}
}
const x = new Cons(5, new Empty) // Cons<number, Empty>
const y = new Cons("hi", new Empty) // Cons<string, Empty>
const z = x.with(y) // Cons<number, Cons<string, Empty>>
您可以在Playground
上查看
我在 Scala 中有一小段值级别和类型级别列表
sealed trait RowSet {
type Append[That <: RowSet] <: RowSet
def with[That <: RowSet](that: That): Append[That]
}
object RowSet {
case object Empty extends RowSet {
type Append[That <: RowSet] = That
override def with[That <: RowSet](that: That): Append[That] = that
}
case class Cons[A, B <: RowSet](head: A, tail: B) extends RowSet { self =>
type Append[That <: RowSet] = Cons[A, tail.Append[That]]
override def with[That <: RowSet](that: That): Append[That] = Cons(head, tail ++ that)
}
}
现在,我正在尝试将它转换为 TypeScript。由于我们没有 Abstract Type Members 功能,我似乎无法找到不需要在某些时候键入 cast 的解决方案。
我目前在 TypeScript 中拥有的内容(也可在 Playground 上获得)
abstract class RowSet {
abstract with<That extends RowSet>(that: That): RowSet
}
type Append<This extends RowSet, That extends RowSet> =
This extends Cons<infer A, infer B> ? Cons<A, Append<B, That>> : That;
class Empty extends RowSet {
public with<That extends RowSet>(that: That): That {
return that;
}
}
class Cons<A, B extends RowSet> extends RowSet {
constructor(public readonly head: A, public readonly tail: B) {
super();
}
public with<That extends RowSet>(that: That): Cons<A, Append<B, That>> {
return new Cons(this.head, this.tail.with(that) as Append<B, That>)
}
}
const x = new Cons(5, new Empty) // Cons<number, Empty>
const y = new Cons("hi", new Empty) // Cons<string, Empty>
const z = x.with(y) // Cons<number, Cons<string, Empty>>
我感兴趣的是我们是否可以避免在此处强制转换:
return new Cons(this.head, this.tail.with(that) as Append<B, That>)
似乎 TypeScript 理解该值实际上是 Append<B, That>
因为它不允许转换为任何不同的东西,例如Append<B, B>
或类似的东西。但是因为我们使用 abtract class RowSet
中的 with
我们最终得到 Cons<A, RowSet>
.
我们能否以不同的方式定义 RowSet,以便 TypeScript 在没有我们帮助的情况下正确推断出所有内容?也许有不同的抽象类型成员转换方式(从 Scala 转换时)?
感谢 Oleg Pyzhcov 的评论,我能够在没有任何手动类型转换的情况下使其工作。 F-bounded 多态性被建议作为解决这个问题的一种方法,事实证明它在这里确实有帮助
解决方案看起来像这样,不需要类型转换,一切都按预期工作
abstract class RowSet<T extends RowSet<T>> {
abstract with<That extends RowSet<That>>(that: That): Append<T, That>
}
type Append<This extends RowSet<This>, That extends RowSet<That>> =
This extends Cons<infer A, infer B> ? Cons<A, Append<B, That>> : That;
class Empty extends RowSet<Empty> {
public with<That extends RowSet<That>>(that: That): That {
return that;
}
}
class Cons<A, B extends RowSet<B>> extends RowSet<Cons<A,B>> {
constructor(public readonly head: A, public readonly tail: B) {
super();
}
public with<That extends RowSet<That>>(that: That): Cons<A, Append<B, That>> {
return new Cons(this.head, this.tail.with(that))
}
}
const x = new Cons(5, new Empty) // Cons<number, Empty>
const y = new Cons("hi", new Empty) // Cons<string, Empty>
const z = x.with(y) // Cons<number, Cons<string, Empty>>
您可以在Playground
上查看