Scala isječak u TypeScript (kako pretvoriti apstraktne tipove članova)
Imam mali isječak liste na razini vrijednosti i razini tipa u Scali
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)
}
}
Sada pokušavam pretvoriti ovu stvar u TypeScript. Budući da nemamo značajku Apstraktnih Tipova Članova, čini se da ne mogu pronaći rješenje koje ne zahtijeva tipiziranje u nekom trenutku.
Ono što trenutno imam u TypeScriptu (također dostupno na Playgroundu)
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>>
Ono što me zanima je možemo li ovdje izbjeći pretvaranje tipa:
return new Cons(this.head, this.tail.with(that) as Append<B, That>)
Čini se da TypeScript razumije da je vrijednost zapravo Append<B, That> jer ne dopušta pretvaranje u bilo što drugo, npr. Append<B, B> ili nešto slično. Ali budući da koristimo with iz abstract class RowSet, završimo s Cons<A, RowSet>.
Možemo li definirati RowSet drugačije, tako da TypeScript ispravno zaključi sve bez naše pomoći? Možda postoji drugačiji način konverzije apstraktnih članova tipova (prilikom konverzije iz Scale)?
Odgovor #1
Zahvaljujući komentaru Olega Pyzhcova uspio sam to učiniti radnim bez bilo kakvog ručnog pretvaranja tipova. F-granična polimorfizam je predložen kao način rješavanja ovog problema, i pokazuje se da ovdje pomaže
Rješenje izgleda ovako, nije potrebno pretvaranje tipova, sve radi kako se očekuje
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>>
Možete to provjeriti na Playgroundu
