Digamos que tengo una clase de TypeScript con una propiedad llamada fecha de birthDate y 3 captadores basados en esta propiedad: year , month y day .
class User { birthDate: Date; get month(): number { return this.birthDate.getMonth(); } get year(): number { return this.birthDate.getFullYear(); } get day(): number { return this.birthDate.getDay(); } } Puedo instanciar un User , establecer una fecha de birthDate y usar los captadores normalmente.
const user = new User(); user.birthDate = new Date(); console.log(user.year, user.month, user.day);Pero, obviamente, no puedo establecer un año directamente porque solo tengo un getter (y no un setter)
// this does not work as expected user.year = 2022 Por otro lado, cuando trato de definir un User con un objeto simple, el compilador de TypeScript genera un error que dice que me faltan propiedades ( year , month y day ):
// this does not works... why? const user: User = { birthDate: new Date() } Mi pregunta es: ¿por qué debería definir estas propiedades en el objeto simple ya que solo son captadores? ¿Cómo puedo crear User s usando objetos simples en este caso?
código de trabajo aquí
Cuando escribes
const user: User = { birthDate: new Date() } usted no crea un objeto User . Simplemente crea un objeto con una propiedad birthDate . Este objeto no cumple con la interfaz implícita en la declaración de User ya que no tiene las propiedades month , year y day .
¿Sabes que Javascript realmente no tiene clases? Solo tiene objetos. En Javascript antiguo puro, donde no era una declaración de class , uno escribió
function User(birthDate) { this.birthDate = birthDate; } User.prototype.getYear = function () { return this.birthDate.getYear(); } User.prototype.getMonth = function () { return this.birthDate.getMonth(); } User.prototype.getDay = function () { return this.birthDate.getDay(); } var user = new User(new Date()); Una función, aquí User() , podría servir como un constructor, y al llamarlo con un new objeto se creó utilizando el objeto prototype de la clase como prototipo (en realidad, está encadenado a través de la propiedad __proto__ del objeto).
La declaración class es simplemente azúcar sintáctica para el patrón anterior.
Mi pregunta es: ¿por qué debería definir estas propiedades en el objeto simple ya que solo son captadores?
Porque su tipo dice que las propiedades de year , month y day de solo lectura existirán en los objetos User , pero no hay ninguna en el objeto que está tratando de asignar a una variable de tipo User .
¿Cómo puedo crear usuarios usando objetos planos en este caso?
Al proporcionar los captadores:
const user: User = { birthDate: new Date(), get month(): number { return user.birthDate.getMonth(); }, get year(): number { return user.birthDate.getFullYear(); }, get day(): number { return user.birthDate.getDay(); }, };Pero usar una función constructora en su lugar parece razonable, por lo que puede reutilizar los captadores en el prototipo.
Para completar: también puede hacerlo haciendo que el objeto sin procesar se herede de un prototipo con esos captadores activados; probablemente sea mejor envolverlo como una función:
interface User { birthDate: Date; readonly month: number; readonly year: number; readonly day: number; } const UserGetters: Partial<User> = { get month(): number { return this.birthDate!.getMonth(); }, get year(): number { return this.birthDate!.getFullYear(); }, get day(): number { return this.birthDate!.getDay(); } }; function makeUser(init: Pick<User, "birthDate">): User { return Object.assign(Object.create(UserGetters), init); } const user = makeUser({birthDate: new Date()}) console.log(user.year); // 2022 (at the moment) (Hice trampa un poco allí con afirmaciones no nulas, ya que sabía que la función makeUser no permitiría que la fecha de birthDate no se especificara o fuera null ).
A algunas personas no les gustan las funciones de constructor, pero les gustan los prototipos, así que esa es una opción para ellos.
La palabra clave aquí es new . En su primer ejemplo, en realidad está instanciando un User . En su segundo ejemplo, simplemente está creando un objeto que se parece a un Usuario (es decir, tiene todos los atributos de un Usuario).
Puede crear un objeto que se parezca a un usuario proporcionando todos los atributos/captadores/etc. Sin embargo, es importante entender cuáles son las ramificaciones de esto. Tenga en cuenta cómo los constructores son diferentes.
class User { birthDate: Date; get month(): number { return this.birthDate.getMonth(); } get year(): number { return this.birthDate.getFullYear(); } get day(): number { return this.birthDate.getDay(); } } const user1 = new User(); user1.birthDate = new Date(2010, 10, 22); console.log("Constructor Name:" + user1.constructor.name); //prints "Constructor Name: User": const user2: User = { birthDate: new Date(2010, 10, 22), get month(): number { return this.birthDate.getMonth(); }, get year(): number { return this.birthDate.getFullYear(); }, get day(): number { return this.birthDate.getDay(); }, } console.log("Constructor Name:" + user2.constructor.name); //prints "Constructor Name: Object"