TL;DR — Un
Type(un tipo definido por el usuario, o UDT) te deja nombrar una forma una sola vez y llevar todos sus campos en una única variable. En lugar de hacer malabares con tres arrays que deben mantenerse alineados por índice, tienes un solo array de registros. UnTypees un tipo de valor: asignarb = acopia todos los campos, así que las dos variables son independientes —lo contrario de una instancia de clase, que se comparte—. Decláralo en la parte superior de un módulo estándar y recurre a un class module solo cuando el registro necesite hacer algo.
' En la parte superior de un módulo estándar, antes de cualquier Sub o Function:
Type Employee
Name As String
Age As Long
Salary As Currency
End Type
Sub Demo()
Dim e As Employee ' una variable contiene los tres campos
e.Name = "Sarah"
e.Age = 34
e.Salary = 65000
Dim copy As Employee
copy = e ' COPIA POR VALOR — cada campo duplicado
copy.Salary = 99000 ' cambiar la copia NO toca a e
MsgBox e.Salary ' sigue siendo 65000
End Sub
La mayoría del código VBA que «lleva la cuenta de varias cosas sobre un mismo elemento»
acaba como arrays paralelos —names(), ages(), salaries()— que tienen que
mantenerse todos en el mismo orden. Ordena uno y olvídate de los demás, y el nombre de
la fila 5 pasa a pertenecer al salario de la fila 12, sin ningún error que te avise. Un
Type lo arregla de raíz: los campos ya no pueden separarse a la deriva, porque viven
en una sola variable.
Lo que aprenderás
- El modelo mental — una fila como una sola variable
- Dónde hay que declarar un
Type(y por qué dentro de unSubfalla) - La regla que sorprende a todo el mundo — un
Typecopia, no comparte - La verdadera recompensa — un solo array de registros en lugar de arrays paralelos
- El límite duro — un
Typeno puede contener comportamiento ni ir en unaCollection Typefrente a class module — cómo elegir
El modelo mental: una fila como una sola variable
Piensa en un Type como una fila de una tabla, ascendida a variable. La fila de una
hoja para un empleado tiene un nombre, una edad y un salario uno al lado del otro; un
Type te da esa misma agrupación en el código. Defines las columnas una vez (Type Employee ... End Type) y cada Dim e As Employee es una fila nueva con esas mismas
casillas.
Esa es toda la idea: deja de pasar valores sueltos que van juntos y pasa una sola cosa
con etiqueta en su lugar. Una función que necesita un empleado recibe un único
argumento Employee, no tres parámetros que tienes que mantener en el orden correcto.
Dónde hay que declarar un Type
Una declaración Type vive a nivel de módulo —en la parte más alta de un módulo
estándar, por encima de cualquier procedimiento. Métela dentro de un Sub y el
proyecto no compilará. Ahí tropieza quien trata a Type como a Dim: Dim es una
instrucción que ejecutas dentro de un procedimiento, pero Type es una declaración
que define una forma para todo el módulo o proyecto.
Dos reglas de colocación que merece la pena memorizar:
Public Type(lo predeterminado a nivel de módulo) hace que el tipo se pueda usar en todo el proyecto. UsaPrivate Typepara dejarlo acotado a un módulo.- No puedes declarar un
Public Typedentro de un class module — VBA lo rechaza con «No se puede definir un tipo público definido por el usuario dentro de un módulo de objeto privado». Guarda tusTypecompartidos en un módulo normal (Module1, o un móduloTypesdedicado), no en una clase ni en el código de una hoja.
La regla que sorprende a todo el mundo: un Type copia, no comparte
Esto es lo más importante que hay que entender sobre un Type, porque es el punto exacto
en el que se diferencia de un objeto. Un Type es un tipo de valor. Cuando escribes
copy = e, VBA duplica todos los campos en una variable nueva e independiente:
Dim a As Employee, b As Employee
a.Salary = 50000
b = a ' copia completa de todos los campos
b.Salary = 70000 ' edita solo b
Debug.Print a.Salary ' 50000 — a queda intacta
Compáralo con una instancia de clase, donde Set b = a
hace de a y b dos nombres para el mismo objeto, así que editar uno edita ambos.
Si alguna vez te ha pillado un objeto que cambia «solo» después de asignarlo en algún
sitio, el comportamiento de copia-al-asignar del Type es el antídoto —y a menudo justo
lo que quieres—.
El único lugar donde la copia no ocurre es al pasarlo a un procedimiento. Un
argumento Type es ByRef por defecto, así que un Sub puede cambiar tu original:
Sub GiveRaise(emp As Employee) ' ByRef por defecto
emp.Salary = emp.Salary * 1.1 ' muta el registro del llamador
End Sub
Pásalo ByVal si quieres que el procedimiento llamado trabaje sobre una copia privada
—la misma regla de ByRef frente a ByVal que gobierna las
variables corrientes.
La verdadera recompensa: un solo array de registros
Aquí es donde un Type se gana su sitio. En lugar de N arrays paralelos que tienes que
mantener alineados a mano, declaras un solo array de registros:
Dim staff(1 To 100) As Employee
staff(1).Name = "Sarah"
staff(1).Salary = 65000
staff(2).Name = "Tom"
staff(2).Salary = 58000
' Ordena, filtra o reordena staff() y cada campo se mueve junto —
' un nombre nunca podrá desincronizarse de su salario otra vez.
El modo de fallo que esto elimina es silencioso y desagradable: con names(), ages()
y salaries() como arrays separados, cualquier operación que
reordene uno —un ordenamiento, una inserción, un borrado— desincronizará a los demás
salvo que te acuerdes de aplicarla a todos de forma idéntica. Con un array de Employee
no hay nada que mantener sincronizado, porque los campos nunca se separaron unos de
otros.
El límite duro: sin comportamiento y sin Collection
Un Type es deliberadamente tonto —guarda datos y nada más—. Eso le da dos límites
duros que te dicen cuándo se te ha quedado pequeño:
- No puede tener métodos. En cuanto tu registro necesita hacer algo —validar su
propia edad, darse formato como cadena, recalcular un total—, un
Typeno puede ayudar. El comportamiento le corresponde a un class module. - No puede ir en una
Collectionni en unDictionary. Esos almacenes guardan valoresVariant, y un UDT no se puede convertir aVariant. Pruebacoll.Add econ unTypey VBA se niega. Si necesitas un almacén de registros que crezca y con clave, eso solo ya es motivo para pasarte a una clase, cuyas instancias sí son objetos que unaCollectionacepta.
Type frente a class module: cómo elegir
Type (UDT) |
Class module | |
|---|---|---|
| Contiene | Solo datos | Datos y comportamiento |
| Categoría | Tipo de valor | Tipo de referencia (objeto) |
b = a |
Copia todos los campos | Set b = a comparte una instancia |
| Se declara en | Módulo estándar, por encima de los procedimientos | Su propio class module |
¿Guardar en una Collection? |
No | Sí |
| Se crea con | Dim e As Employee |
Set e = New clsEmployee |
| Mejor cuando | Los campos solo necesitan viajar juntos | El registro necesita métodos o debe vivir en una colección |
Mi regla general: si los campos solo necesitan mantenerse juntos y quieres copias
baratas e independientes, un Type es la herramienta correcta —tirar de una clase aquí
es sobreingeniería—. Pásate a una clase en cuanto el registro necesite comportamiento, o
en cuanto necesites tener muchos de ellos en una Collection. Todo lo que queda en medio
es cuestión de criterio, y «empieza con el Type y promuévelo luego» es un valor por
defecto perfectamente válido.
Cómo ayuda ExcelMaster
Decidir entre arrays paralelos, un Type y una clase completa —y luego acordarte de que
un Type copia mientras que un objeto comparte— es justo la clase de decisión de
modelado que es fácil equivocar de forma sutil. Los errores que provoca (un array
desincronizado, un objeto que muta «solo») compilan sin problemas y solo asoman más
tarde.
ExcelMaster te deja
describir los datos en su lugar. Di «necesito llevar el nombre, la edad y el salario de
una lista de empleados y ordenarlos por salario» y modelará los registros por ti —un
array de un Type, con el ordenamiento aplicado para que nada se desincronice— y te
explicará, en el código, cuándo habría elegido una clase en su lugar. Consigues la
estructura correcta sin tener que retener en la cabeza todas las reglas de valor frente a
referencia.
Preguntas frecuentes
¿Qué es un Type en VBA?
Un Type (tipo definido por el usuario, o UDT) es una estructura de datos propia que
agrupa varios campos relacionados bajo un mismo nombre —por ejemplo un Employee con
Name, Age y Salary—. Lo declaras una vez con Type ... End Type en la parte
superior de un módulo estándar y luego lo usas como cualquier otro tipo: Dim e As Employee. Deja que los valores relacionados viajen juntos como una sola variable en
lugar de como variables separadas y fáciles de desincronizar.
¿Cuál es la diferencia entre un Type y un class module en VBA?
Un Type guarda solo datos y es un tipo de valor —asignar uno a otro copia todos
los campos, así que ambos son independientes—. Un class module
guarda datos y comportamiento (métodos) y es un tipo de referencia —Set b = a
hace que ambos nombres apunten a la misma instancia—. Usa un Type para un registro
pasivo; usa una clase cuando el registro necesite métodos o deba guardarse en una
Collection.
¿Dónde declaro un Type en VBA?
A nivel de módulo, por encima de cualquier procedimiento, en un módulo estándar.
Declarar un Type dentro de un Sub o Function es un error de compilación. Usa
Public Type (lo predeterminado) para compartirlo en todo el proyecto, o Private Type
para limitarlo a un módulo. No puedes declarar un Public Type dentro de un class module.
¿Puedo guardar un Type de VBA en una Collection o un Dictionary?
No. Una Collection y un Dictionary guardan valores Variant, y un tipo definido por
el usuario no se puede convertir a Variant, así que coll.Add someType falla. Si
necesitas un almacén de registros que crezca o con clave, usa un
class module en su lugar —sus instancias son objetos que una
Collection acepta— o guarda los registros en un array del Type.
¿Asignar una variable Type a otra copia o comparte los datos?
Copia. Como un Type es un tipo de valor, b = a duplica todos los campos en una
variable independiente; cambiar b después no afecta a a. Esto es lo contrario de los
objetos (instancias de clase), donde Set b = a comparte una sola instancia. La única
excepción es pasar un Type a un procedimiento, que es ByRef por defecto y por tanto
puede mutar el registro del llamador.
Probado en
Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 04/08/2026.
Guías relacionadas: VBA Class Module · VBA Property · VBA Data Types · VBA Array · VBA ByRef frente a ByVal
