Recientemente, he estado tratando de crear una biblioteca de tipos para la biblioteca escrita en JS. Cuando declaré todos los espacios de nombres, clases e interfaces, algunas clases comenzaron a darme un error TS2417 . Verifiqué si había algún problema con la anulación no válida de métodos o propiedades, pero no pude encontrar nada. Después de un tiempo, descubrí que la clase problemática tenía el mismo nombre que uno de los espacios de nombres (por ejemplo, clase AB y espacio de nombres AB ). Pero esto no causó ningún problema. El problema era que la clase padre (de la clase problemática) tenía, al igual que la clase problemática, un espacio de nombres que se llamaba exactamente igual y este espacio de nombres y el espacio de nombres de la clase problemática tenían una clase, que se llamaba exactamente igual, pero tenía interfaz diferente (es muy difícil para mí describir el problema, así que simulé aquí ).
Entonces la pregunta es, ¿qué causa este problema?
En este ejemplo, la clase problemática es AC , pero tiene problemas debido a constructores incompatibles de las clases ACX y ABX .
declare namespace A { class B { } namespace B { class X { } } class C extends AB { } namespace C { class X { constructor(x: number) } } }(A continuación prescindiré del espacio de nombres A )
Con la excepción de la firma de construcción , el lado static de una class se verifica para la sustitución de la misma manera que el lado de la instancia; entonces, si tiene class X { static prop: string = "" } , entonces no puede tener class Y extends X { static prop: number = 2} . Cuando dice que class Y extends X , está declarando que, entre otras cosas, Y.prop se puede asignar a X.prop . Consulte microsoft/TypeScript#4628 para obtener más información. No a todos les gusta esta restricción, pero está ahí.
Eso significa que lo siguiente debería funcionar sin errores:
const X: typeof BX = CX; // should be okay new X(); // should be okayPero una implementación concreta de sus clases podría conducir fácilmente a un error de tiempo de ejecución:
class B { static X = class { } } class C extends B { static X = class { constructor(x: number) { x.toFixed(); } } } Aquí, CX es un constructor de clases que requiere la entrada de un number . Pero BX es un constructor de clases que no requiere ninguna entrada. Y si trata el primero como si fuera el último, en tiempo de ejecución llamará al método toFixed() en undefined . Ups.
Y es por eso que la class C extends B genera un error de compilación; para protegerlo contra los problemas de sustitución que surgen del lado estático de una clase.