typescript - Hur kapslar man in en join- eller flatten-metod i en länkad lista?
Jag har en grundläggande länkad lista byggd i TypeScript med en diskriminerad union.
type ListType<T> = {
Kind: "Cons",
Head: T,
Tail: List<T>
} | {
Kind: "Empty"
}
type ListOperations<T> = {
reduce: <U>(this: List<T>, f: (state: U, x: T) => U, accumulator: U) => U
map: <U>(this: List<T>, f: (_: T) => U) => List<U>
reverse: (this: List<T>) => List<T>
concat: (this: List<T>, l: List<T>) => List<T>
toArray: (this: List<T>) => T[]
join: (this: List<List<T>>) => List<T>
}
type List<T> = ListType<T> & ListOperations<T>
Jag har också några konstruktorer för både tom och cons:
export const Cons = <T>(head: T, tail: List<T>): List<T> => ({
Kind: "Cons",
Head: head,
Tail: tail,
...ListOperations()
})
export const Empty = <T>(): List<T> => ({
Kind: "Empty",
...ListOperations()
})
Och slutligen har jag en implementering av de olika metoderna:
const ListOperations = <T>(): ListOperations<T> => ({
reduce: function <U>(this: List<T>, f: (state: U, x: T) => U, accumulator: U): U {
return this.Kind == "Empty" ? accumulator : this.Tail.reduce(f, f(accumulator, this.Head))
},
map: function <U>(this: List<T>, f: (_: T) => U): List<U> {
return this.reduce((s, x) => Cons(f(x), s), Empty())
},
reverse: function (this: List<T>): List<T> {
return this.reduce((s, x) => Cons(x, s), Empty())
},
concat: function (this: List<T>, l: List<T>): List<T> {
return this.reverse().reduce((s, x) => Cons(x, s), l)
},
toArray: function (this: List<T>): T[] {
return this.reduce<T[]>((s, x) => s.concat([x]), [])
},
join: function (this: List<List<T>>): List<T> {
return this.reduce((s, x) => s.concat(x), Empty())
}
})
Allt fungerar bra, men jag får ett kompileringsfel när jag försöker köra följande:
let x = Cons(1, Cons(2, Cons(3, Cons(4, Empty()))))
let y = x.map(x => x + 4)
let z = Cons(x, Cons(y, Empty()))
z.join()
‘this’-kontexten av typen
List<List<number>>kan inte tilldelas metodens ‘this’ av typenList<List<List<number>>>.
Detta beror på join-metoden (eller flatten som vissa av er kanske kallar den). När jag skriver join utanför List-typen fungerar det, så min fråga är: Finns det ett sätt att uttryckligen tala om för kompilatorn att this behöver vara av typen List<List<T>>?
Jag har redan försökt använda extends
join: function <T1 extends List<T>>(this: List<T1>): List<T>Svar #1
Det beror på att din lista är en List<T>, medan T själv är en List<T>. Den korrekta typningen skulle vara:
join(this: List<T>): T {
För att sedan säkerställa att T själv är en lista, använd en konditionell typ:
join(this: T extends List<*> ? List<T> : "Only nested lists can be joined!"): T
