el tipo A no se puede asignar al tipo B con los tipos condicionales de TypeScript 2.8
esto es en realidad un seguimiento sobre TypeScript 2.8 Exclude: ¿es posible potenciar el tipo 'partition'? -- Estoy teniendo problemas al introducir tipos condicionales en mi código.
Tenga en cuenta que los tipos condicionales son nuevos en TypeScript 2.8, por lo que actualmente esto debe construirse con TypeScript 2.8rc.
He reducido mi problema actual a este caso de prueba bastante pequeño:
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;
}
}
La nueva línea es la primera sobrecarga de partition2. Si la comentas, todo se compila bien.
Si compilas esto con --strict, falla con un error de compilación bastante desconcertante:
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>>>>'.
Realmente no sé qué hacer para que esto funcione :-(
Respuesta #1
Como mencioné en los comentarios, esto parece un error del compilador (o al menos una limitación de diseño). La mínima reproducción que puedo encontrar se ve así:
interface A<T> {
bat: B<A<T>>;
}
interface B<T> extends A<T> {
bat: B<B<T>>;
boom: true
}
Todo está bien aquí. Todos los parámetros de tipo están en posiciones covariantes, lo que significa que si Y es un subtipo de X, entonces A<Y> es un subtipo de A<X>, y B<Y> es un subtipo de B<X>. Y dado que B<T> es un subtipo de A<T>, se puede deducir que, específicamente, B<B<T>> es un subtipo de B<A<T>>. Por lo tanto, está bien que la propiedad bat de B reduzca B<A<T>> a B<B<T>>.
Ahora observa lo que sucede cuando añadimos un tipo condicional (que no se evalúa inmediatamente) a 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
}
Explota. boom sin duda seguirá siendo de tipo true, pero de alguna manera el tipo condicional diferido está causando problemas. Por el error, está claro que el compilador está siguiendo si B<T> es compatible con A<T> verificando si B<B<T>> es compatible con B<A<T>> verificando si B<B<B<T>>> es compatible con B<B<A<T>>> por... uy.
Así que hay algún tipo de regresión infinita en el verificador de tipos que se detiene temprano... eso es bueno, ya que de lo contrario el compilador simplemente se colgaría. Pero cuando se detiene, decide que B<T> no es compatible con A<T>, lo cual es malo para nosotros (aunque una regla como "detenerse con éxito" probablemente también sea mala).
Entonces, mi sugerencia es hacer lo que hiciste y registrar el problema en GitHub. Veremos qué dicen al respecto allí.
No estoy seguro de si hay soluciones realmente fáciles. Editaré esta respuesta si encuentro una.
¡Buena suerte!
Respuesta #2
El problema es que Seq<T>.tail devuelve Option<Seq<T>> mientras que Vector<T>.tail devuelve Option<Vector<T>>. Estos dos tipos no son compatibles, como el compilador nos indica extensamente. La solución más simple es cambiar Seq<T> para que devuelva un Option del mismo tipo que el tipo actual, sea cual sea ese tipo. Esto significa que la implementación de tail en Vector<T> devolverá Option<Vector<T>> como se espera, pero será compatible con la definición de tail en Seq. Podemos hacer esto usando el tipo this:
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>>
