Búsqueda de sitios web

¿Cómo agregar la restricción "newable" para un parámetro genérico en TypeScript?


Ya sé cómo agregar la restricción "newable" (es decir, que tiene constructor) para los argumentos de funciones (como el argumento para la función foo a continuación), pero la misma técnica no se aplica a los parámetros de tipo genérico.

¿Por qué es eso y cómo arreglarlo?

type NoParameterCtor<T> = { new(): T }

function foo<T>(ctor: NoParameterCtor<T>) { }

interface Bar<T extends NoParameterCtor<T>> { }

class Zoo { }

foo(Zoo) 
// no compiler error

class Zar implements Bar<Zoo> { }
// Type 'Zoo' does not satisfy the constraint 'NoParameterCtor<Zoo>'

Respuesta #1

Como se menciona en los comentarios, T extends NoParameterCtor<T> es una restricción inusual que significa "T es un constructor que crea nuevas instancias de sí mismo". A menos que esté tratando de describir constructores autorreplicantes, esto no es lo que quiere decir.

Si solo quieres que T sea "cualquiera que pueda ser instanciado", entonces no necesitas preocuparte por el tipo de instancia. Suponiendo que estés usando TS3.0 o posterior, puedes usar unknown para significar cualquier tipo, aunque también puedes usar any. Así que quizás quieras que Bar sea

interface Bar<T extends NoParameterCtor<unknown>> { }

Sin embargo, lo siguiente aún no funcionará:

class Zar implements Bar<Zoo> { } // error! 
// Zoo does not satisfy the constraint NoParameterCtor<unknown>

Eso se debe a que el tipo Zoo no se puede instanciar; es el tipo de instancia de la clase Zoo. No sé si estás confundido acerca de la diferencia entre valores nombrados y tipos nombrados en TypeScript, pero estás en buena compañía si es así. En resumen, class Zoo {} introduce un tipo llamado Zoo, que es el tipo de las instancias de la clase, y un valor llamado Zoo, que es el constructor de dichas instancias. Y el tipo del valor Zoo no es el tipo Zoo. Para referirse al tipo del valor constructor Zoo, necesitas usar typeof Foo en su lugar:

class Zar implements Bar<typeof Zoo> { } // okay

También asumo que has eliminado el contenido de Bar, Zar y Zoo porque no son relevantes aquí. Pero solo para aclarar, las interfaces vacías coinciden con casi todo porque TypeScript usa tipado estructural. Si Bar necesita acceso al tipo de instancia de T, entonces puedes usar el alias de tipo incorporado de la biblioteca InstanceType<> para obtenerlo:

interface Bar<T extends NoParameterCtor<unknown>> {
   theConstructor: T,
   theInstance: InstanceType<T>
}

class Zar implements Bar<typeof Zoo> { 
  theConstructor = Zoo; // the constructor
  theInstance = new Zoo(); // an instance
}

Espero que eso ayude. ¡Buena suerte!

Artículos relacionados