typ A kan inte tilldelas typ B med TypeScript 2.8 villkorliga typer
detta är faktiskt en uppföljning på typescript 2.8 Exclude: möjligt att superladda 'partition'-typen? -- jag har problem med att införa villkorliga typer i min kod.
Observera att villkorstyper är nya med TypeScript 2.8, så för tillfället måste detta byggas med TypeScript 2.8rc.
Jag har reducerat mitt nuvarande problem till detta ganska lilla testfall:
export class Option<T> {
toVector(): Vector<T> {
return <any>undefined;
}
}
interface Seq<T> {
tail(): Option<Seq<T>>;
}
class Vector<T> implements Seq<T> {
tail(): Option<Vector<T>> {
return <any>undefined;
}
// the next line breaks the compilation
partition2<U extends T>(predicate:(v:T)=>v is U): [Vector<U>,Vector<Exclude<T,U>>];
partition2(predicate:(x:T)=>boolean): [Vector<T>,Vector<T>];
partition2<U extends T>(predicate:(v:T)=>boolean): [Vector<U>,Vector<any>] {
return <any>undefined;
}
}
Den nya raden är den första överlagringen för partition2. Om du kommenterar den, byggs allt utan problem.
Om du kompilerar detta med --strict misslyckas det med ett ganska förbryllande byggfel:
t.ts(13,5): error TS2416: Property 'tail' in type 'Vector<T>' is not assignable to the same property in base type 'Seq<T>'.
Type '() => Option<Vector<T>>' is not assignable to type '() => Option<Seq<T>>'.
Type 'Option<Vector<T>>' is not assignable to type 'Option<Seq<T>>'.
Types of property 'toVector' are incompatible.
Type '() => Vector<Vector<T>>' is not assignable to type '() => Vector<Seq<T>>'.
Type 'Vector<Vector<T>>' is not assignable to type 'Vector<Seq<T>>'.
Types of property 'tail' are incompatible.
Type '() => Option<Vector<Vector<T>>>' is not assignable to type '() => Option<Vector<Seq<T>>>'.
Type 'Option<Vector<Vector<T>>>' is not assignable to type 'Option<Vector<Seq<T>>>'.
Types of property 'toVector' are incompatible.
Type '() => Vector<Vector<Vector<T>>>' is not assignable to type '() => Vector<Vector<Seq<T>>>'.
Type 'Vector<Vector<Vector<T>>>' is not assignable to type 'Vector<Vector<Seq<T>>>'.
Types of property 'tail' are incompatible.
Type '() => Option<Vector<Vector<Vector<T>>>>' is not assignable to type '() => Option<Vector<Vector<Seq<T>>>>'.
Type 'Option<Vector<Vector<Vector<T>>>>' is not assignable to type 'Option<Vector<Vector<Seq<T>>>>'.
Types of property 'toVector' are incompatible.
Type '() => Vector<Vector<Vector<Vector<T>>>>' is not assignable to type '() => Vector<Vector<Vector<Seq<T>>>>'.
Type 'Vector<Vector<Vector<Vector<T>>>>' is not assignable to type 'Vector<Vector<Vector<Seq<T>>>>'.
Types of property 'tail' are incompatible.
Type '() => Option<Vector<Vector<Vector<Vector<T>>>>>' is not assignable to type '() => Option<Vector<Vector<Vector<Seq<T>>>>>'.
Type 'Option<Vector<Vector<Vector<Vector<T>>>>>' is not assignable to type 'Option<Vector<Vector<Vector<Seq<T>>>>>'.
Types of property 'toVector' are incompatible.
Type '() => Vector<Vector<Vector<Vector<Vector<T>>>>>' is not assignable to type '() => Vector<Vector<Vector<Vector<Seq<T>>>>>'.
Type 'Vector<Vector<Vector<Vector<Vector<T>>>>>' is not assignable to type 'Vector<Vector<Vector<Vector<Seq<T>>>>>'.
Type 'Vector<Vector<Vector<Seq<T>>>>' is not assignable to type 'Vector<Vector<Vector<Vector<T>>>>'.
Jag vet verkligen inte vad jag ska göra för att få det här att fungera :-(
Svar #1
Som jag nämnde i kommentarerna, ser detta ut som ett kompilatorfel (eller åtminstone en designbegränsning). Den minimala reproduktionen jag kan hitta ser ut så här:
interface A<T> {
bat: B<A<T>>;
}
interface B<T> extends A<T> {
bat: B<B<T>>;
boom: true
}
Allt är okej här. Alla typparametrar är i kovarianta positioner, vilket betyder att om Y är en subtyp av X, så är A<Y> en subtyp av A<X>, och B<Y> är en subtyp av B<X>. Och eftersom B<T> är en subtyp av A<T>, kan du härleda att, specifikt, B<B<T>> är en subtyp av B<A<T>>. Därför är det okej att bat-egenskapen hos B begränsar B<A<T>> till B<B<T>>.
Titta nu på vad som händer när vi lägger till en villkorlig typ (som inte utvärderas omedelbart) till B:
interface A<T> {
bat: B<A<T>>;
}
interface B<T> extends A<T> {
// ^ error
// Interface 'B<T>' incorrectly extends interface 'A<T>'.
bat: B<B<T>>;
boom: T extends any ? true : true
}
Det exploderar. boom kommer utan tvekan fortfarande att vara av typen true, men på något sätt orsakar den uppskjutna villkorliga typen problem. Från felet är det tydligt att kompilatorn försöker ta reda på om B<T> är kompatibel med A<T> genom att kontrollera om B<B<T>> är kompatibel med B<A<T>> genom att kontrollera om B<B<B<T>>> är kompatibel med B<B<A<T>>> genom att... oj då.
Så det finns någon form av oändlig återgång i typkontrollören som avbryter tidigt... det är bra, eftersom annars skulle kompilatorn bara hänga. Men när den avbryter bestämmer den att B<T> inte är kompatibel med A<T>, vilket är dåligt för oss (även om en regel som "avbryt med framgång" förmodligen också är dålig).
Så, mitt förslag är att göra som du gjorde och rapportera problemet på GitHub. Vi får se vad de säger om det där.
Jag är inte säker på om det finns några riktigt enkla lösningar. Jag kommer att redigera detta svar om jag hittar en.
Lycka till!
Svar #2
Problemet är att Seq<T>.tail returnerar Option<Seq<T>> medan Vector<T>.tail returnerar Option<Vector<T>>. Dessa två typer är inte kompatibla, som kompilatorn detaljerat meddelar oss. Den enklaste lösningen är att ändra Seq<T> så att det returnerar en Option av samma typ som den aktuella typen, oavsett vilken typ det är. Detta betyder att implementationen av tail i Vector<T> kommer att returnera Option<Vector<T>> som förväntat, men kommer att vara kompatibel med definitionen av tail i Seq. Vi kan göra detta med hjälp av this-typen:
export class Option<T> {
toVector(): Vector<T> {
return <any>undefined;
}
}
interface Seq<T> {
tail(): Option<this>;
}
class Vector<T> implements Seq<T> {
tail(): Option<this> {
return <any>undefined;
}
// the next line breaks the compilation
partition2<U extends T>(predicate:(v:T)=>v is U): [Vector<U>,Vector<Exclude<T,U>>];
partition2(predicate:(x:T)=>boolean): [Vector<T>,Vector<T>];
partition2<U extends T>(predicate:(v:T)=>boolean): [Vector<U>,Vector<any>] {
return <any>undefined;
}
}
declare var v: Vector<number>;
var d = v.tail().toVector() // will be Vector<Vector<number>>
