[ffigen] Make type polymorphic (#290)

* Make Type polymorphic

* Migrate tests

* Fix analysis errors

* Fix analysis
diff --git a/pkgs/ffigen/lib/src/code_generator.dart b/pkgs/ffigen/lib/src/code_generator.dart
index 6944957..a0c5d7e 100644
--- a/pkgs/ffigen/lib/src/code_generator.dart
+++ b/pkgs/ffigen/lib/src/code_generator.dart
@@ -10,11 +10,15 @@
 export 'code_generator/constant.dart';
 export 'code_generator/enum_class.dart';
 export 'code_generator/func.dart';
+export 'code_generator/func_type.dart';
 export 'code_generator/global.dart';
+export 'code_generator/handle.dart';
 export 'code_generator/imports.dart';
 export 'code_generator/library.dart';
+export 'code_generator/native_type.dart';
 export 'code_generator/objc_interface.dart';
-export 'code_generator/struc.dart';
+export 'code_generator/pointer.dart';
+export 'code_generator/struct.dart';
 export 'code_generator/type.dart';
 export 'code_generator/typealias.dart';
 export 'code_generator/union.dart';
diff --git a/pkgs/ffigen/lib/src/code_generator/binding_string.dart b/pkgs/ffigen/lib/src/code_generator/binding_string.dart
index 031bf5a..84c4da8 100644
--- a/pkgs/ffigen/lib/src/code_generator/binding_string.dart
+++ b/pkgs/ffigen/lib/src/code_generator/binding_string.dart
@@ -17,7 +17,7 @@
 /// A [BindingString]'s type.
 enum BindingStringType {
   func,
-  struc,
+  struct,
   union,
   constant,
   global,
diff --git a/pkgs/ffigen/lib/src/code_generator/compound.dart b/pkgs/ffigen/lib/src/code_generator/compound.dart
index 067e64f..2656e04 100644
--- a/pkgs/ffigen/lib/src/code_generator/compound.dart
+++ b/pkgs/ffigen/lib/src/code_generator/compound.dart
@@ -11,17 +11,18 @@
 enum CompoundType { struct, union }
 
 /// A binding for Compound type - Struct/Union.
-abstract class Compound extends NoLookUpBinding {
+abstract class Compound extends BindingType {
   /// Marker for if a struct definition is complete.
   ///
   /// A function can be safely pass this struct by value if it's complete.
-  bool isInComplete;
+  bool isIncomplete;
 
   List<Member> members;
 
   bool get isOpaque => members.isEmpty;
 
-  /// Value for `@Packed(X)` annotation. Can be null(no packing), 1, 2, 4, 8, 16.
+  /// Value for `@Packed(X)` annotation. Can be null (no packing), 1, 2, 4, 8,
+  /// or 16.
   ///
   /// Only supported for [CompoundType.struct].
   int? pack;
@@ -38,7 +39,7 @@
     String? originalName,
     required String name,
     required this.compoundType,
-    this.isInComplete = false,
+    this.isIncomplete = false,
     this.pack,
     String? dartDoc,
     List<Member>? members,
@@ -55,18 +56,18 @@
     String? usr,
     String? originalName,
     required String name,
-    bool isInComplete = false,
+    bool isIncomplete = false,
     int? pack,
     String? dartDoc,
     List<Member>? members,
   }) {
     switch (type) {
       case CompoundType.struct:
-        return Struc(
+        return Struct(
           usr: usr,
           originalName: originalName,
           name: name,
-          isInComplete: isInComplete,
+          isIncomplete: isIncomplete,
           pack: pack,
           dartDoc: dartDoc,
           members: members,
@@ -76,7 +77,7 @@
           usr: usr,
           originalName: originalName,
           name: name,
-          isInComplete: isInComplete,
+          isIncomplete: isIncomplete,
           pack: pack,
           dartDoc: dartDoc,
           members: members,
@@ -87,16 +88,17 @@
   List<int> _getArrayDimensionLengths(Type type) {
     final array = <int>[];
     var startType = type;
-    while (startType.broadType == BroadType.ConstantArray) {
-      array.add(startType.length!);
-      startType = startType.child!;
+    while (startType is ConstantArray) {
+      array.add(startType.length);
+      startType = startType.child;
     }
     return array;
   }
 
   String _getInlineArrayTypeString(Type type, Writer w) {
-    if (type.broadType == BroadType.ConstantArray) {
-      return '${w.ffiLibraryPrefix}.Array<${_getInlineArrayTypeString(type.child!, w)}>';
+    if (type is ConstantArray) {
+      return '${w.ffiLibraryPrefix}.Array<'
+          '${_getInlineArrayTypeString(type.child, w)}>';
     }
     return type.getCType(w);
   }
@@ -125,23 +127,23 @@
     }
     final dartClassName = isStruct ? 'Struct' : 'Union';
     // Write class declaration.
-    s.write(
-        'class $enclosingClassName extends ${w.ffiLibraryPrefix}.${isOpaque ? 'Opaque' : dartClassName}{\n');
+    s.write('class $enclosingClassName extends ');
+    s.write('${w.ffiLibraryPrefix}.${isOpaque ? 'Opaque' : dartClassName}{\n');
     const depth = '  ';
     for (final m in members) {
       final memberName = localUniqueNamer.makeUnique(m.name);
-      if (m.type.broadType == BroadType.ConstantArray) {
-        s.write(
-            '$depth@${w.ffiLibraryPrefix}.Array.multi(${_getArrayDimensionLengths(m.type)})\n');
-        s.write(
-            '${depth}external ${_getInlineArrayTypeString(m.type, w)} $memberName;\n\n');
+      if (m.type is ConstantArray) {
+        s.write('$depth@${w.ffiLibraryPrefix}.Array.multi(');
+        s.write('${_getArrayDimensionLengths(m.type)})\n');
+        s.write('${depth}external ${_getInlineArrayTypeString(m.type, w)} ');
+        s.write('$memberName;\n\n');
       } else {
         if (m.dartDoc != null) {
           s.write(depth + '/// ');
           s.writeAll(m.dartDoc!.split('\n'), '\n' + depth + '/// ');
           s.write('\n');
         }
-        if (!m.type.sameDartAndCType(w)) {
+        if (!sameDartAndCType(m.type, w)) {
           s.write('$depth@${m.type.getCType(w)}()\n');
         }
         s.write('${depth}external ${m.type.getDartType(w)} $memberName;\n\n');
@@ -150,7 +152,7 @@
     s.write('}\n\n');
 
     return BindingString(
-        type: isStruct ? BindingStringType.struc : BindingStringType.union,
+        type: isStruct ? BindingStringType.struct : BindingStringType.union,
         string: s.toString());
   }
 
@@ -163,6 +165,12 @@
       m.type.addDependencies(dependencies);
     }
   }
+
+  @override
+  bool get isIncompleteCompound => isIncomplete;
+
+  @override
+  String getCType(Writer w) => name;
 }
 
 class Member {
diff --git a/pkgs/ffigen/lib/src/code_generator/enum_class.dart b/pkgs/ffigen/lib/src/code_generator/enum_class.dart
index 2765aef..07b9eed 100644
--- a/pkgs/ffigen/lib/src/code_generator/enum_class.dart
+++ b/pkgs/ffigen/lib/src/code_generator/enum_class.dart
@@ -4,6 +4,8 @@
 
 import 'binding.dart';
 import 'binding_string.dart';
+import 'native_type.dart';
+import 'type.dart';
 import 'utils.dart';
 import 'writer.dart';
 
@@ -21,7 +23,9 @@
 ///   static const banana = 10;
 /// }
 /// ```
-class EnumClass extends NoLookUpBinding {
+class EnumClass extends BindingType {
+  static final nativeType = NativeType(SupportedNativeType.Int32);
+
   final List<EnumConstant> enumConstants;
 
   EnumClass({
@@ -75,6 +79,12 @@
 
     dependencies.add(this);
   }
+
+  @override
+  String getCType(Writer w) => nativeType.getCType(w);
+
+  @override
+  String getDartType(Writer w) => nativeType.getDartType(w);
 }
 
 /// Represents a single value in an enum.
diff --git a/pkgs/ffigen/lib/src/code_generator/func.dart b/pkgs/ffigen/lib/src/code_generator/func.dart
index d33f135..815c303 100644
--- a/pkgs/ffigen/lib/src/code_generator/func.dart
+++ b/pkgs/ffigen/lib/src/code_generator/func.dart
@@ -33,7 +33,8 @@
   final bool isLeaf;
 
   /// Contains typealias for function type if [exposeFunctionTypedefs] is true.
-  Typealias? _exposedCFunctionTypealias, _exposedDartFunctionTypealias;
+  Typealias? _exposedCFunctionTypealias;
+  Typealias? _exposedDartFunctionTypealias;
 
   /// [originalName] is looked up in dynamic library, if not
   /// provided, takes the value of [name].
@@ -68,11 +69,11 @@
     if (exposeFunctionTypedefs) {
       _exposedCFunctionTypealias = Typealias(
         name: 'Native$upperCaseName',
-        type: Type.functionType(functionType),
+        type: functionType,
       );
       _exposedDartFunctionTypealias = Typealias(
         name: 'Dart$upperCaseName',
-        type: Type.functionType(functionType),
+        type: functionType,
         useDartType: true,
       );
     }
@@ -94,9 +95,7 @@
       p.name = paramNamer.makeUnique(p.name);
     }
     // Write enclosing function.
-    if (w.dartBool &&
-        functionType.returnType.getBaseTypealiasType().broadType ==
-            BroadType.Boolean) {
+    if (w.dartBool && functionType.returnType.typealiasType is BooleanType) {
       // Use bool return type in enclosing function.
       s.write('bool $enclosingFuncName(\n');
     } else {
@@ -104,8 +103,7 @@
           '${functionType.returnType.getDartType(w)} $enclosingFuncName(\n');
     }
     for (final p in functionType.parameters) {
-      if (w.dartBool &&
-          p.type.getBaseTypealiasType().broadType == BroadType.Boolean) {
+      if (w.dartBool && p.type.typealiasType is BooleanType) {
         // Use bool parameter type in enclosing function.
         s.write('  bool ${p.name},\n');
       } else {
@@ -117,15 +115,14 @@
 
     s.write('(\n');
     for (final p in functionType.parameters) {
-      if (w.dartBool &&
-          p.type.getBaseTypealiasType().broadType == BroadType.Boolean) {
+      if (w.dartBool && p.type.typealiasType is BooleanType) {
         // Convert bool parameter to int before calling.
         s.write('    ${p.name}?1:0,\n');
       } else {
         s.write('    ${p.name},\n');
       }
     }
-    if (w.dartBool && functionType.returnType.broadType == BroadType.Boolean) {
+    if (w.dartBool && functionType.returnType.typealiasType is BooleanType) {
       // Convert int return type to bool.
       s.write('  )!=0;\n');
     } else {
@@ -180,9 +177,7 @@
 
   Parameter({String? originalName, this.name = '', required Type type})
       : originalName = originalName ?? name,
-        // A type with broadtype [BroadType.NativeFunction] is wrapped with a
-        // pointer because this is a shorthand used in C for Pointer to function.
-        type = type.getBaseTypealiasType().broadType == BroadType.NativeFunction
-            ? Type.pointer(type)
-            : type;
+        // A [NativeFunc] is wrapped with a pointer because this is a shorthand
+        // used in C for Pointer to function.
+        type = type.typealiasType is NativeFunc ? PointerType(type) : type;
 }
diff --git a/pkgs/ffigen/lib/src/code_generator/func_type.dart b/pkgs/ffigen/lib/src/code_generator/func_type.dart
new file mode 100644
index 0000000..7f40502
--- /dev/null
+++ b/pkgs/ffigen/lib/src/code_generator/func_type.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/src/code_generator.dart';
+
+import 'writer.dart';
+
+/// Represents a function type.
+class FunctionType extends Type {
+  final Type returnType;
+  final List<Parameter> parameters;
+
+  FunctionType({
+    required this.returnType,
+    required this.parameters,
+  });
+
+  String _getTypeString(
+      bool writeArgumentNames, String Function(Type) typeToString) {
+    final sb = StringBuffer();
+
+    // Write return Type.
+    sb.write(typeToString(returnType));
+
+    // Write Function.
+    sb.write(' Function(');
+    sb.write(parameters.map<String>((p) {
+      return '${typeToString(p.type)} ${writeArgumentNames ? p.name : ""}';
+    }).join(', '));
+    sb.write(')');
+
+    return sb.toString();
+  }
+
+  @override
+  String getCType(Writer w, {bool writeArgumentNames = true}) =>
+      _getTypeString(writeArgumentNames, (Type t) => t.getCType(w));
+
+  @override
+  String getDartType(Writer w, {bool writeArgumentNames = true}) =>
+      _getTypeString(writeArgumentNames, (Type t) => t.getDartType(w));
+
+  @override
+  String toString() => _getTypeString(false, (Type t) => t.toString());
+
+  @override
+  void addDependencies(Set<Binding> dependencies) {
+    returnType.addDependencies(dependencies);
+    for (final p in parameters) {
+      p.type.addDependencies(dependencies);
+    }
+  }
+}
+
+/// Represents a NativeFunction<Function>.
+class NativeFunc extends Type {
+  final Type type;
+
+  NativeFunc(this.type);
+
+  @override
+  void addDependencies(Set<Binding> dependencies) {
+    type.addDependencies(dependencies);
+  }
+
+  @override
+  String getCType(Writer w) =>
+      '${w.ffiLibraryPrefix}.NativeFunction<${type.getCType(w)}>';
+
+  @override
+  String getDartType(Writer w) =>
+      '${w.ffiLibraryPrefix}.NativeFunction<${type.getCType(w)}>';
+
+  @override
+  String toString() => 'NativeFunction<${type.toString()}>';
+}
diff --git a/pkgs/ffigen/lib/src/code_generator/global.dart b/pkgs/ffigen/lib/src/code_generator/global.dart
index df4c7f0..579d8dd 100644
--- a/pkgs/ffigen/lib/src/code_generator/global.dart
+++ b/pkgs/ffigen/lib/src/code_generator/global.dart
@@ -4,6 +4,7 @@
 
 import 'binding.dart';
 import 'binding_string.dart';
+import 'compound.dart';
 import 'type.dart';
 import 'utils.dart';
 import 'writer.dart';
@@ -49,9 +50,9 @@
 
     s.write(
         "late final ${w.ffiLibraryPrefix}.Pointer<$cType> $pointerName = ${w.lookupFuncIdentifier}<$cType>('$originalName');\n\n");
-    final baseTypealiasType = type.getBaseTypealiasType();
-    if (baseTypealiasType.broadType == BroadType.Compound) {
-      if (baseTypealiasType.compound!.isOpaque) {
+    final baseTypealiasType = type.typealiasType;
+    if (baseTypealiasType is Compound) {
+      if (baseTypealiasType.isOpaque) {
         s.write(
             '${w.ffiLibraryPrefix}.Pointer<$cType> get $globalVarName => $pointerName;\n\n');
       } else {
diff --git a/pkgs/ffigen/lib/src/code_generator/handle.dart b/pkgs/ffigen/lib/src/code_generator/handle.dart
new file mode 100644
index 0000000..a1b2d47
--- /dev/null
+++ b/pkgs/ffigen/lib/src/code_generator/handle.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/src/code_generator.dart';
+
+import 'writer.dart';
+
+/// Represents a Dart_Handle.
+class HandleType extends Type {
+  const HandleType._();
+  static const _handle = HandleType._();
+  factory HandleType() => _handle;
+
+  @override
+  String getCType(Writer w) => '${w.ffiLibraryPrefix}.Handle';
+
+  @override
+  String getDartType(Writer w) => 'Object';
+
+  @override
+  String toString() => 'Handle';
+}
diff --git a/pkgs/ffigen/lib/src/code_generator/imports.dart b/pkgs/ffigen/lib/src/code_generator/imports.dart
index e620626..1a51611 100644
--- a/pkgs/ffigen/lib/src/code_generator/imports.dart
+++ b/pkgs/ffigen/lib/src/code_generator/imports.dart
@@ -2,7 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'struc.dart';
+import 'struct.dart';
+import 'type.dart';
+import 'writer.dart';
 
 /// A library import which will be written as an import in the generated file.
 class LibraryImport {
@@ -22,12 +24,21 @@
 }
 
 /// An imported type which will be used in the generated code.
-class ImportedType {
+class ImportedType extends Type {
   final LibraryImport libraryImport;
   final String cType;
   final String dartType;
 
   ImportedType(this.libraryImport, this.cType, this.dartType);
+
+  @override
+  String getCType(Writer w) => '${libraryImport.prefix}.$cType';
+
+  @override
+  String getDartType(Writer w) => cType == dartType ? getCType(w) : dartType;
+
+  @override
+  String toString() => '${libraryImport.name}.$cType';
 }
 
 final ffiImport = LibraryImport('ffi', 'dart:ffi');
@@ -54,5 +65,5 @@
 final sizeType = ImportedType(ffiPkgImport, 'Size', 'int');
 final wCharType = ImportedType(ffiPkgImport, 'WChar', 'int');
 
-final objCObjectType = Struc(name: 'ObjCObject');
-final objCSelType = Struc(name: 'ObjCSel');
+final objCObjectType = Struct(name: 'ObjCObject');
+final objCSelType = Struct(name: 'ObjCSel');
diff --git a/pkgs/ffigen/lib/src/code_generator/library.dart b/pkgs/ffigen/lib/src/code_generator/library.dart
index 20c0e24..e00215c 100644
--- a/pkgs/ffigen/lib/src/code_generator/library.dart
+++ b/pkgs/ffigen/lib/src/code_generator/library.dart
@@ -11,7 +11,7 @@
 import 'package:path/path.dart' as p;
 import 'binding.dart';
 import 'imports.dart';
-import 'struc.dart';
+import 'struct.dart';
 import 'utils.dart';
 import 'writer.dart';
 
@@ -59,7 +59,7 @@
     // conflicts have been handled so that users can target the generated names.
     if (packingOverride != null) {
       for (final b in this.bindings) {
-        if (b is Struc && packingOverride.isOverriden(b.name)) {
+        if (b is Struct && packingOverride.isOverriden(b.name)) {
           b.pack = packingOverride.getOverridenPackValue(b.name);
         }
       }
diff --git a/pkgs/ffigen/lib/src/code_generator/native_type.dart b/pkgs/ffigen/lib/src/code_generator/native_type.dart
new file mode 100644
index 0000000..e51581d
--- /dev/null
+++ b/pkgs/ffigen/lib/src/code_generator/native_type.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/src/code_generator.dart';
+
+import 'writer.dart';
+
+enum SupportedNativeType {
+  Void,
+  Char,
+  Int8,
+  Int16,
+  Int32,
+  Int64,
+  Uint8,
+  Uint16,
+  Uint32,
+  Uint64,
+  Float,
+  Double,
+  IntPtr,
+}
+
+/// Represents a primitive native type, such as float.
+class NativeType extends Type {
+  static const _primitives = <SupportedNativeType, NativeType>{
+    SupportedNativeType.Void: NativeType._('Void', 'void'),
+    SupportedNativeType.Char: NativeType._('Uint8', 'int'),
+    SupportedNativeType.Int8: NativeType._('Int8', 'int'),
+    SupportedNativeType.Int16: NativeType._('Int16', 'int'),
+    SupportedNativeType.Int32: NativeType._('Int32', 'int'),
+    SupportedNativeType.Int64: NativeType._('Int64', 'int'),
+    SupportedNativeType.Uint8: NativeType._('Uint8', 'int'),
+    SupportedNativeType.Uint16: NativeType._('Uint16', 'int'),
+    SupportedNativeType.Uint32: NativeType._('Uint32', 'int'),
+    SupportedNativeType.Uint64: NativeType._('Uint64', 'int'),
+    SupportedNativeType.Float: NativeType._('Float', 'double'),
+    SupportedNativeType.Double: NativeType._('Double', 'double'),
+    SupportedNativeType.IntPtr: NativeType._('IntPtr', 'int'),
+  };
+
+  final String _cType;
+  final String _dartType;
+
+  const NativeType._(this._cType, this._dartType);
+
+  factory NativeType(SupportedNativeType type) => _primitives[type]!;
+
+  @override
+  String getCType(Writer w) => '${w.ffiLibraryPrefix}.$_cType';
+
+  @override
+  String getDartType(Writer w) => _dartType;
+
+  @override
+  String toString() => _cType;
+}
+
+class BooleanType extends NativeType {
+  // Booleans are treated as uint8.
+  const BooleanType._() : super._('Uint8', 'int');
+  static const _boolean = BooleanType._();
+  factory BooleanType() => _boolean;
+
+  @override
+  String toString() => 'bool';
+}
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
index bc00b7c..2372485 100644
--- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
+++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
@@ -32,20 +32,16 @@
   late final _registerNameFunc = Func(
     name: '_sel_registerName',
     originalName: 'sel_registerName',
-    returnType: Type.pointer(Type.struct(objCSelType)),
-    parameters: [
-      Parameter(name: 'str', type: Type.pointer(Type.importedType(charType)))
-    ],
+    returnType: PointerType(objCSelType),
+    parameters: [Parameter(name: 'str', type: PointerType(charType))],
   );
   late final String registerName;
 
   late final _getClassFunc = Func(
     name: '_objc_getClass',
     originalName: 'objc_getClass',
-    returnType: Type.pointer(Type.struct(objCObjectType)),
-    parameters: [
-      Parameter(name: 'str', type: Type.pointer(Type.importedType(charType)))
-    ],
+    returnType: PointerType(objCObjectType),
+    parameters: [Parameter(name: 'str', type: PointerType(charType))],
   );
   late final String getClass;
 
@@ -62,9 +58,9 @@
       originalName: 'objc_msgSend',
       returnType: returnType,
       parameters: [
-        Parameter(name: 'obj', type: Type.pointer(Type.struct(objCObjectType))),
-        Parameter(name: 'sel', type: Type.pointer(Type.struct(objCSelType))),
-        for (final p in params) Parameter(name: p.name, type: p.type.type),
+        Parameter(name: 'obj', type: PointerType(objCObjectType)),
+        Parameter(name: 'sel', type: PointerType(objCSelType)),
+        for (final p in params) Parameter(name: p.name, type: p.type),
       ],
     );
     return _msgSendFuncs[key]!;
@@ -111,7 +107,7 @@
 
 final _builtInFunctions = _ObjCBuiltInFunctions();
 
-class ObjCInterface extends NoLookUpBinding {
+class ObjCInterface extends BindingType {
   ObjCInterface? superType;
   final methods = <ObjCMethod>[];
   bool filled = false;
@@ -146,8 +142,8 @@
     final natLib = w.className;
 
     _builtInFunctions.ensureUtilsExist(w, s);
-    final objType = Type.pointer(Type.struct(objCObjectType)).getCType(w);
-    final selType = Type.pointer(Type.struct(objCSelType)).getCType(w);
+    final objType = PointerType(objCObjectType).getCType(w);
+    final selType = PointerType(objCSelType).getCType(w);
 
     // Class declaration.
     s.write('class $name ');
@@ -179,7 +175,7 @@
       }
       s.write('  ');
       if (isStatic) s.write('static ');
-      s.write('${m.returnType!.getConvertedType(w, name)} ');
+      s.write('${_getConvertedType(m.returnType!, w, name)} ');
       if (m.kind == ObjCMethodKind.propertyGetter) s.write('get ');
       if (m.kind == ObjCMethodKind.propertySetter) s.write('set ');
       s.write(methodName);
@@ -196,7 +192,7 @@
           } else {
             s.write(', ');
           }
-          s.write('${p.type.getConvertedType(w, name)} ${p.name}');
+          s.write('${_getConvertedType(p.type, w, name)} ${p.name}');
         }
         s.write(')');
       }
@@ -209,17 +205,17 @@
       }
       s.write('    $selName ??= '
           '${_builtInFunctions.registerName}(_lib, "${m.originalName}");\n');
-      final convertReturn = m.returnType!.needsConverting;
+      final convertReturn = _needsConverting(m.returnType!);
       s.write('    ${convertReturn ? 'final _ret = ' : 'return '}');
       s.write('_lib.${m.msgSend!.name}(');
       s.write(isStatic ? '_class!' : '_id');
       s.write(', $selName!');
       for (final p in m.params) {
-        s.write(', ${p.type.doArgConversion(p.name)}');
+        s.write(', ${_doArgConversion(p.type, p.name)}');
       }
       s.write(');\n');
       if (convertReturn) {
-        final result = m.returnType!.doReturnConversion('_ret', name, '_lib');
+        final result = _doReturnConversion(m.returnType!, '_ret', name, '_lib');
         s.write('    return $result;');
       }
 
@@ -261,6 +257,46 @@
       classMethods[method.originalName] ??= method;
     }
   }
+
+  @override
+  String getCType(Writer w) => PointerType(objCObjectType).getCType(w);
+
+  bool _isObject(Type type) =>
+      type is PointerType && type.child == objCObjectType;
+
+  bool _isInstanceType(Type type) =>
+      type is Typealias &&
+      type.originalName == 'instancetype' &&
+      _isObject(type.type);
+
+  // Utils for converting between the internal types passed to native code, and
+  // the external types visible to the user. For example, ObjCInterfaces are
+  // passed to native as Pointer<ObjCObject>, but the user sees the Dart wrapper
+  // class. These methods need to be kept in sync.
+  bool _needsConverting(Type type) =>
+      type is ObjCInterface || _isObject(type) || _isInstanceType(type);
+
+  String _getConvertedType(Type type, Writer w, String enclosingClass) {
+    if (type is ObjCInterface) return type.name;
+    if (_isObject(type)) return 'NSObject';
+    if (_isInstanceType(type)) return enclosingClass;
+    return type.getDartType(w);
+  }
+
+  String _doArgConversion(Type type, String value) {
+    if (type is ObjCInterface || _isObject(type) || _isInstanceType(type)) {
+      return '$value._id';
+    }
+    return value;
+  }
+
+  String _doReturnConversion(
+      Type type, String value, String enclosingClass, String library) {
+    if (type is ObjCInterface) return '${type.name}._($value, $library)';
+    if (_isObject(type)) return 'NSObject._($value, $library)';
+    if (_isInstanceType(type)) return '$enclosingClass._($value, $library)';
+    return value;
+  }
 }
 
 enum ObjCMethodKind {
@@ -280,7 +316,7 @@
   final String? dartDoc;
   final String originalName;
   final ObjCProperty? property;
-  ObjCMethodType? returnType;
+  Type? returnType;
   final params = <ObjCMethodParam>[];
   final ObjCMethodKind kind;
   Func? msgSend;
@@ -293,11 +329,11 @@
   });
 
   void addDependencies(Set<Binding> dependencies) {
-    returnType!.type.addDependencies(dependencies);
+    returnType!.addDependencies(dependencies);
     for (final p in params) {
-      p.type.type.addDependencies(dependencies);
+      p.type.addDependencies(dependencies);
     }
-    msgSend = _builtInFunctions.getMsgSendFunc(returnType!.type, params);
+    msgSend = _builtInFunctions.getMsgSendFunc(returnType!, params);
   }
 
   String _getDartMethodName(UniqueNamer uniqueNamer) {
@@ -323,53 +359,7 @@
 }
 
 class ObjCMethodParam {
-  final ObjCMethodType type;
-  final String name;
-  ObjCMethodParam(Type t, this.name) : type = ObjCMethodType(t);
-}
-
-// Wrapper around Type with helper methods for converting between the internal
-// types passed to native code, and the external types visible to the user. For
-// example, ObjCInterfaces are passed to native as Pointer<ObjCObject>, but the
-// user sees the Dart wrapper class.
-class ObjCMethodType {
   final Type type;
-  ObjCMethodType(this.type);
-
-  bool get isObject {
-    if (type.broadType != BroadType.Pointer) return false;
-    final child = type.child!;
-    if (child.broadType != BroadType.Compound) return false;
-    return child.compound == objCObjectType;
-  }
-
-  bool get isInstanceType {
-    if (type.broadType != BroadType.Typealias) return false;
-    final alias = type.typealias!;
-    if (alias.name != 'instancetype') return false;
-    return ObjCMethodType(alias.type).isObject;
-  }
-
-  bool get isInterface => type.broadType == BroadType.ObjCInterface;
-  bool get needsConverting => isInterface || isObject || isInstanceType;
-
-  String getConvertedType(Writer w, String enclosingClass) {
-    if (isInterface) return type.objCInterface!.name;
-    if (isObject) return 'NSObject';
-    if (isInstanceType) return enclosingClass;
-    return type.getDartType(w);
-  }
-
-  String doArgConversion(String value) {
-    if (isInterface || isObject || isInstanceType) return '$value._id';
-    return value;
-  }
-
-  String doReturnConversion(
-      String value, String enclosingClass, String library) {
-    if (isInterface) return '${type.objCInterface!.name}._($value, $library)';
-    if (isObject) return 'NSObject._($value, $library)';
-    if (isInstanceType) return '$enclosingClass._($value, $library)';
-    return value;
-  }
+  final String name;
+  ObjCMethodParam(this.type, this.name);
 }
diff --git a/pkgs/ffigen/lib/src/code_generator/pointer.dart b/pkgs/ffigen/lib/src/code_generator/pointer.dart
new file mode 100644
index 0000000..445c360
--- /dev/null
+++ b/pkgs/ffigen/lib/src/code_generator/pointer.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/src/code_generator.dart';
+
+import 'writer.dart';
+
+/// Represents a pointer.
+class PointerType extends Type {
+  final Type child;
+  PointerType(this.child);
+
+  @override
+  void addDependencies(Set<Binding> dependencies) {
+    child.addDependencies(dependencies);
+  }
+
+  @override
+  Type get baseType => child.baseType;
+
+  @override
+  String getCType(Writer w) =>
+      '${w.ffiLibraryPrefix}.Pointer<${child.getCType(w)}>';
+
+  @override
+  String toString() => '*$child';
+}
+
+/// Represents a constant array, which has a fixed size.
+class ConstantArray extends PointerType {
+  final int length;
+  ConstantArray(this.length, Type child) : super(child);
+
+  @override
+  Type get baseArrayType => child.baseArrayType;
+
+  @override
+  bool get isIncompleteCompound => baseArrayType.isIncompleteCompound;
+
+  @override
+  String toString() => '$child[$length]';
+}
+
+/// Represents an incomplete array, which has an unknown size.
+class IncompleteArray extends PointerType {
+  IncompleteArray(Type child) : super(child);
+
+  @override
+  Type get baseArrayType => child.baseArrayType;
+
+  @override
+  String toString() => '$child[]';
+}
diff --git a/pkgs/ffigen/lib/src/code_generator/struc.dart b/pkgs/ffigen/lib/src/code_generator/struct.dart
similarity index 89%
rename from pkgs/ffigen/lib/src/code_generator/struc.dart
rename to pkgs/ffigen/lib/src/code_generator/struct.dart
index 4f06369..f430d0f 100644
--- a/pkgs/ffigen/lib/src/code_generator/struc.dart
+++ b/pkgs/ffigen/lib/src/code_generator/struct.dart
@@ -28,12 +28,12 @@
 ///
 /// }
 /// ```
-class Struc extends Compound {
-  Struc({
+class Struct extends Compound {
+  Struct({
     String? usr,
     String? originalName,
     required String name,
-    bool isInComplete = false,
+    bool isIncomplete = false,
     int? pack,
     String? dartDoc,
     List<Member>? members,
@@ -42,7 +42,7 @@
           originalName: originalName,
           name: name,
           dartDoc: dartDoc,
-          isInComplete: isInComplete,
+          isIncomplete: isIncomplete,
           members: members,
           pack: pack,
           compoundType: CompoundType.struct,
diff --git a/pkgs/ffigen/lib/src/code_generator/type.dart b/pkgs/ffigen/lib/src/code_generator/type.dart
index a708167..af580b3 100644
--- a/pkgs/ffigen/lib/src/code_generator/type.dart
+++ b/pkgs/ffigen/lib/src/code_generator/type.dart
@@ -6,420 +6,95 @@
 
 import 'writer.dart';
 
-class _SubType {
-  final String c;
-  final String dart;
-
-  const _SubType({required this.c, required this.dart});
-}
-
-enum SupportedNativeType {
-  Void,
-  Char,
-  Int8,
-  Int16,
-  Int32,
-  Int64,
-  Uint8,
-  Uint16,
-  Uint32,
-  Uint64,
-  Float,
-  Double,
-  IntPtr,
-}
-
-/// The basic types in which all types can be broadly classified into.
-enum BroadType {
-  Boolean,
-  NativeType,
-  Pointer,
-  Compound,
-  NativeFunction,
-
-  /// Represents a function type.
-  FunctionType,
-
-  /// Represents an imported type.
-  ImportedType,
-
-  /// Represents a typealias.
-  Typealias,
-
-  /// Represents a Dart_Handle.
-  Handle,
-
-  Enum,
-
-  /// Represents an Array type.
-  ConstantArray,
-  IncompleteArray,
-
-  /// Represents an Objective C interface.
-  ObjCInterface,
-
-  /// Used as a marker, so that declarations having these can exclude them.
-  Unimplemented,
-}
-
 /// Type class for return types, variable types, etc.
-class Type {
-  static const _primitives = <SupportedNativeType, _SubType>{
-    SupportedNativeType.Void: _SubType(c: 'Void', dart: 'void'),
-    SupportedNativeType.Char: _SubType(c: 'Uint8', dart: 'int'),
-    SupportedNativeType.Int8: _SubType(c: 'Int8', dart: 'int'),
-    SupportedNativeType.Int16: _SubType(c: 'Int16', dart: 'int'),
-    SupportedNativeType.Int32: _SubType(c: 'Int32', dart: 'int'),
-    SupportedNativeType.Int64: _SubType(c: 'Int64', dart: 'int'),
-    SupportedNativeType.Uint8: _SubType(c: 'Uint8', dart: 'int'),
-    SupportedNativeType.Uint16: _SubType(c: 'Uint16', dart: 'int'),
-    SupportedNativeType.Uint32: _SubType(c: 'Uint32', dart: 'int'),
-    SupportedNativeType.Uint64: _SubType(c: 'Uint64', dart: 'int'),
-    SupportedNativeType.Float: _SubType(c: 'Float', dart: 'double'),
-    SupportedNativeType.Double: _SubType(c: 'Double', dart: 'double'),
-    SupportedNativeType.IntPtr: _SubType(c: 'IntPtr', dart: 'int'),
-  };
-
-  /// Enum type is mapped to [SupportedNativeType.Int32].
-  static const enumNativeType = SupportedNativeType.Int32;
-
-  /// Reference to the [Compound] binding this type refers to.
-  Compound? compound;
-
-  /// Reference to the [NativeFunc] this type refers to.
-  NativeFunc? nativeFunc;
-
-  /// Reference to the [Typealias] this type refers to.
-  Typealias? typealias;
-
-  /// Reference to the [FunctionType] this type refers to.
-  FunctionType? functionType;
-
-  /// Reference to the [EnumClass] this type refers to.
-  EnumClass? enumClass;
-
-  /// Reference to the [ImportedType] this type refers to.
-  ImportedType? importedType;
-
-  /// Reference to the [ObjCInterface] this type refers to.
-  ObjCInterface? objCInterface;
-
-  /// For providing [SupportedNativeType] only.
-  final SupportedNativeType? nativeType;
-
-  /// The BroadType of this Type.
-  final BroadType broadType;
-
-  /// Child Type, e.g Pointer(Parent) to Int(Child), or Child Type of an Array.
-  final Type? child;
-
-  /// For ConstantArray and IncompleteArray type.
-  final int? length;
-
-  /// For storing cursor type info for an unimplemented type.
-  String? unimplementedReason;
-
-  Type._({
-    required this.broadType,
-    this.child,
-    this.compound,
-    this.enumClass,
-    this.nativeType,
-    this.nativeFunc,
-    this.typealias,
-    this.functionType,
-    this.importedType,
-    this.objCInterface,
-    this.length,
-    this.unimplementedReason,
-  });
-
-  factory Type.pointer(Type child) {
-    return Type._(broadType: BroadType.Pointer, child: child);
-  }
-  factory Type.compound(Compound compound) {
-    return Type._(broadType: BroadType.Compound, compound: compound);
-  }
-  factory Type.struct(Struc struc) {
-    return Type._(broadType: BroadType.Compound, compound: struc);
-  }
-  factory Type.union(Union union) {
-    return Type._(broadType: BroadType.Compound, compound: union);
-  }
-  factory Type.enumClass(EnumClass enumClass) {
-    return Type._(broadType: BroadType.Enum, enumClass: enumClass);
-  }
-  factory Type.functionType(FunctionType functionType) {
-    return Type._(
-        broadType: BroadType.FunctionType, functionType: functionType);
-  }
-  factory Type.importedType(ImportedType importedType) {
-    return Type._(
-        broadType: BroadType.ImportedType, importedType: importedType);
-  }
-  factory Type.nativeFunc(NativeFunc nativeFunc) {
-    return Type._(broadType: BroadType.NativeFunction, nativeFunc: nativeFunc);
-  }
-  factory Type.typealias(Typealias typealias) {
-    return Type._(broadType: BroadType.Typealias, typealias: typealias);
-  }
-  factory Type.nativeType(SupportedNativeType nativeType) {
-    return Type._(broadType: BroadType.NativeType, nativeType: nativeType);
-  }
-  factory Type.constantArray(int length, Type elementType) {
-    return Type._(
-      broadType: BroadType.ConstantArray,
-      child: elementType,
-      length: length,
-    );
-  }
-  factory Type.incompleteArray(Type elementType) {
-    return Type._(
-      broadType: BroadType.IncompleteArray,
-      child: elementType,
-    );
-  }
-  factory Type.boolean() {
-    return Type._(
-      broadType: BroadType.Boolean,
-    );
-  }
-  factory Type.unimplemented(String reason) {
-    return Type._(
-        broadType: BroadType.Unimplemented, unimplementedReason: reason);
-  }
-  factory Type.handle() {
-    return Type._(broadType: BroadType.Handle);
-  }
-  factory Type.objCInterface(ObjCInterface objCInterface) {
-    return Type._(
-        broadType: BroadType.ObjCInterface, objCInterface: objCInterface);
-  }
+///
+/// Implementers should extend either Type, or BindingType if the type is also a
+/// binding, and override at least getCType and toString.
+abstract class Type {
+  const Type();
 
   /// Get all dependencies of this type and save them in [dependencies].
-  void addDependencies(Set<Binding> dependencies) {
-    switch (broadType) {
-      case BroadType.Compound:
-        return compound!.addDependencies(dependencies);
-      case BroadType.NativeFunction:
-        return nativeFunc!.addDependencies(dependencies);
-      case BroadType.FunctionType:
-        return functionType!.addDependencies(dependencies);
-      case BroadType.Typealias:
-        return typealias!.addDependencies(dependencies);
-      case BroadType.Enum:
-        return enumClass!.addDependencies(dependencies);
-      case BroadType.ObjCInterface:
-        return objCInterface!.addDependencies(dependencies);
-      default:
-        if (child != null) {
-          return child!.addDependencies(dependencies);
-        }
-    }
-  }
+  void addDependencies(Set<Binding> dependencies) {}
 
   /// Get base type for any type.
   ///
   /// E.g int** has base [Type] of int.
   /// double[2][3] has base [Type] of double.
-  Type getBaseType() {
-    if (child != null) {
-      return child!.getBaseType();
-    } else {
-      return this;
-    }
-  }
+  Type get baseType => this;
 
   /// Get base Array type.
   ///
   /// Returns itself if it's not an Array Type.
-  Type getBaseArrayType() {
-    if (broadType == BroadType.ConstantArray ||
-        broadType == BroadType.IncompleteArray) {
-      return child!.getBaseArrayType();
-    } else {
-      return this;
-    }
-  }
+  Type get baseArrayType => this;
 
   /// Get base typealias type.
   ///
   /// Returns itself if it's not a Typealias.
-  Type getBaseTypealiasType() {
-    if (broadType == BroadType.Typealias) {
-      return typealias!.type.getBaseTypealiasType();
-    } else {
-      return this;
-    }
-  }
-
-  /// Function to check if the dart and C type string are same.
-  bool sameDartAndCType(Writer w) => getCType(w) == getDartType(w);
+  Type get typealiasType => this;
 
   /// Returns true if the type is a [Compound] and is incomplete.
-  bool get isIncompleteCompound {
-    final baseTypealiasType = getBaseTypealiasType();
-    if (baseTypealiasType == this) {
-      return (broadType == BroadType.Compound &&
-              compound != null &&
-              compound!.isInComplete) ||
-          (broadType == BroadType.ConstantArray &&
-              getBaseArrayType().isIncompleteCompound);
-    } else {
-      return baseTypealiasType.isIncompleteCompound;
-    }
-  }
+  bool get isIncompleteCompound => false;
 
-  String getCType(Writer w) {
-    switch (broadType) {
-      case BroadType.NativeType:
-        return '${w.ffiLibraryPrefix}.${_primitives[nativeType!]!.c}';
-      case BroadType.Pointer:
-        return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
-      case BroadType.Compound:
-        return compound!.name;
-      case BroadType.Enum:
-        return '${w.ffiLibraryPrefix}.${_primitives[enumNativeType]!.c}';
-      case BroadType.NativeFunction:
-        return '${w.ffiLibraryPrefix}.NativeFunction<${nativeFunc!.type.getCType(w)}>';
-      case BroadType.IncompleteArray:
-        // Array parameters are treated as Pointers in C.
-        return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
-      case BroadType.ConstantArray:
-        // Array parameters are treated as Pointers in C.
-        return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
-      case BroadType.Boolean:
-        // Booleans are treated as uint8.
-        return '${w.ffiLibraryPrefix}.${_primitives[SupportedNativeType.Uint8]!.c}';
-      case BroadType.Handle:
-        return '${w.ffiLibraryPrefix}.Handle';
-      case BroadType.FunctionType:
-        return functionType!.getCType(w);
-      case BroadType.ImportedType:
-        return '${importedType!.libraryImport.prefix}.${importedType!.cType}';
-      case BroadType.ObjCInterface:
-        return Type.pointer(Type.struct(objCObjectType)).getCType(w);
-      case BroadType.Typealias:
-        return typealias!.name;
-      case BroadType.Unimplemented:
-        throw UnimplementedError('C type unknown for ${broadType.toString()}');
-    }
-  }
+  /// Returns the C type of the Type. This is the FFI compatible type that is
+  /// passed to native code.
+  String getCType(Writer w) => throw 'No mapping for type: $this';
 
-  String getDartType(Writer w) {
-    switch (broadType) {
-      case BroadType.NativeType:
-        return _primitives[nativeType!]!.dart;
-      case BroadType.Pointer:
-        return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
-      case BroadType.Compound:
-        return compound!.name;
-      case BroadType.Enum:
-        return _primitives[enumNativeType]!.dart;
-      case BroadType.NativeFunction:
-        return '${w.ffiLibraryPrefix}.NativeFunction<${nativeFunc!.type.getDartType(w)}>';
-      case BroadType.IncompleteArray:
-        // Array parameters are treated as Pointers in C.
-        return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
-      case BroadType.ConstantArray:
-        // Array parameters are treated as Pointers in C.
-        return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
-      case BroadType.Boolean:
-        // Booleans are treated as uint8.
-        return _primitives[SupportedNativeType.Uint8]!.dart;
-      case BroadType.Handle:
-        return 'Object';
-      case BroadType.FunctionType:
-        return functionType!.getDartType(w);
-      case BroadType.ImportedType:
-        if (importedType!.cType == importedType!.dartType) {
-          return getCType(w);
-        } else {
-          return importedType!.dartType;
-        }
-      case BroadType.ObjCInterface:
-        return getCType(w);
-      case BroadType.Typealias:
-        // Typealias cannot be used by name in Dart types unless both the C and
-        // Dart type of the underlying types are same.
-        if (typealias!.type.sameDartAndCType(w)) {
-          return typealias!.name;
-        } else {
-          return typealias!.type.getDartType(w);
-        }
-      case BroadType.Unimplemented:
-        throw UnimplementedError(
-            'dart type unknown for ${broadType.toString()}');
-    }
-  }
+  /// Returns the Dart type of the Type. This is the user visible type that is
+  /// passed to Dart code.
+  String getDartType(Writer w) => getCType(w);
+
+  /// Returns the string representation of the Type, for debugging purposes
+  /// only. This string should not be printed as generated code.
+  @override
+  String toString();
+}
+
+/// Function to check if the dart and C type string are same.
+bool sameDartAndCType(Type t, Writer w) => t.getCType(w) == t.getDartType(w);
+
+/// Base class for all Type bindings.
+///
+/// Since Dart doesn't have multiple inheritance, this type exists so that we
+/// don't have to reimplement the default methods in all the classes that want
+/// to extend both NoLookUpBinding and Type.
+abstract class BindingType extends NoLookUpBinding implements Type {
+  BindingType({
+    String? usr,
+    String? originalName,
+    required String name,
+    String? dartDoc,
+  }) : super(
+          usr: usr,
+          originalName: originalName,
+          name: name,
+          dartDoc: dartDoc,
+        );
 
   @override
-  String toString() {
-    return 'Type: $broadType';
-  }
+  Type get baseType => this;
+
+  @override
+  Type get baseArrayType => this;
+
+  @override
+  Type get typealiasType => this;
+
+  @override
+  bool get isIncompleteCompound => false;
+
+  @override
+  String getDartType(Writer w) => getCType(w);
+
+  @override
+  String toString() => originalName;
 }
 
-/// Represents a function type.
-class FunctionType {
-  final Type returnType;
-  final List<Parameter> parameters;
+/// Represents an unimplemented type. Used as a marker, so that declarations
+/// having these can exclude them.
+class UnimplementedType extends Type {
+  String reason;
+  UnimplementedType(this.reason);
 
-  FunctionType({
-    required this.returnType,
-    required this.parameters,
-  });
-
-  String getCType(Writer w, {bool writeArgumentNames = true}) {
-    final sb = StringBuffer();
-
-    // Write return Type.
-    sb.write(returnType.getCType(w));
-
-    // Write Function.
-    sb.write(' Function(');
-    sb.write(parameters.map<String>((p) {
-      return '${p.type.getCType(w)} ${writeArgumentNames ? p.name : ""}';
-    }).join(', '));
-    sb.write(')');
-
-    return sb.toString();
-  }
-
-  String getDartType(Writer w, {bool writeArgumentNames = true}) {
-    final sb = StringBuffer();
-
-    // Write return Type.
-    sb.write(returnType.getDartType(w));
-
-    // Write Function.
-    sb.write(' Function(');
-    sb.write(parameters.map<String>((p) {
-      return '${p.type.getDartType(w)} ${writeArgumentNames ? p.name : ""}';
-    }).join(', '));
-    sb.write(')');
-
-    return sb.toString();
-  }
-
-  void addDependencies(Set<Binding> dependencies) {
-    returnType.addDependencies(dependencies);
-    for (final p in parameters) {
-      p.type.addDependencies(dependencies);
-    }
-  }
-}
-
-/// Represents a NativeFunction<Function>.
-class NativeFunc {
-  final Type type;
-
-  NativeFunc.fromFunctionType(FunctionType functionType)
-      : type = Type.functionType(functionType);
-
-  NativeFunc.fromFunctionTypealias(Typealias typealias)
-      : type = Type.typealias(typealias);
-
-  void addDependencies(Set<Binding> dependencies) {
-    type.addDependencies(dependencies);
-  }
+  @override
+  String toString() => '(Unimplemented: $reason)';
 }
diff --git a/pkgs/ffigen/lib/src/code_generator/typealias.dart b/pkgs/ffigen/lib/src/code_generator/typealias.dart
index ea8ef95..f83c0ba 100644
--- a/pkgs/ffigen/lib/src/code_generator/typealias.dart
+++ b/pkgs/ffigen/lib/src/code_generator/typealias.dart
@@ -14,7 +14,7 @@
 /// typedef $name = $type;
 /// );
 /// ```
-class Typealias extends NoLookUpBinding {
+class Typealias extends BindingType {
   final Type type;
   final bool _useDartType;
 
@@ -51,9 +51,29 @@
     if (dartDoc != null) {
       sb.write(makeDartDoc(dartDoc!));
     }
-    sb.write(
-        'typedef $name = ${_useDartType ? type.getDartType(w) : type.getCType(w)};\n');
+    sb.write('typedef $name = ');
+    sb.write('${_useDartType ? type.getDartType(w) : type.getCType(w)};\n');
     return BindingString(
         type: BindingStringType.typeDef, string: sb.toString());
   }
+
+  @override
+  Type get typealiasType => type.typealiasType;
+
+  @override
+  bool get isIncompleteCompound => type.isIncompleteCompound;
+
+  @override
+  String getCType(Writer w) => name;
+
+  @override
+  String getDartType(Writer w) {
+    // Typealias cannot be used by name in Dart types unless both the C and Dart
+    // type of the underlying types are same.
+    if (sameDartAndCType(type, w)) {
+      return name;
+    } else {
+      return type.getDartType(w);
+    }
+  }
 }
diff --git a/pkgs/ffigen/lib/src/code_generator/union.dart b/pkgs/ffigen/lib/src/code_generator/union.dart
index a8a0861..fc7be96 100644
--- a/pkgs/ffigen/lib/src/code_generator/union.dart
+++ b/pkgs/ffigen/lib/src/code_generator/union.dart
@@ -32,7 +32,7 @@
     String? usr,
     String? originalName,
     required String name,
-    bool isInComplete = false,
+    bool isIncomplete = false,
     int? pack,
     String? dartDoc,
     List<Member>? members,
@@ -41,7 +41,7 @@
           originalName: originalName,
           name: name,
           dartDoc: dartDoc,
-          isInComplete: isInComplete,
+          isIncomplete: isIncomplete,
           members: members,
           pack: pack,
           compoundType: CompoundType.union,
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
index f1eb651..49e2418 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
@@ -27,7 +27,7 @@
 
   _ParsedCompound(this.compound);
 
-  bool get isInComplete =>
+  bool get isIncomplete =>
       unimplementedMemberType ||
       flexibleArrayMember ||
       bitFieldMember ||
@@ -45,7 +45,7 @@
   int alignment = 0;
 
   bool get _isPacked {
-    if (!hasAttr || isInComplete) return false;
+    if (!hasAttr || isIncomplete) return false;
     if (hasPackedAttr) return true;
 
     return maxChildAlignment > alignment;
@@ -185,7 +185,7 @@
   _stack.pop();
 
   _logger.finest(
-      'Opaque: ${parsed.isInComplete}, HasAttr: ${parsed.hasAttr}, AlignValue: ${parsed.alignment}, MaxChildAlignValue: ${parsed.maxChildAlignment}, PackValue: ${parsed.packValue}.');
+      'Opaque: ${parsed.isIncomplete}, HasAttr: ${parsed.hasAttr}, AlignValue: ${parsed.alignment}, MaxChildAlignValue: ${parsed.maxChildAlignment}, PackValue: ${parsed.packValue}.');
   compound.pack = parsed.packValue;
 
   visitChildrenResultChecker(resultCode);
@@ -218,13 +218,13 @@
   }
 
   // Clear all members if declaration is incomplete.
-  if (parsed.isInComplete) {
+  if (parsed.isIncomplete) {
     compound.members.clear();
   }
 
   // C allows empty structs/union, but it's undefined behaviour at runtine.
   // So we need to mark a declaration incomplete if it has no members.
-  compound.isInComplete = parsed.isInComplete || compound.members.isEmpty;
+  compound.isIncomplete = parsed.isIncomplete || compound.members.isEmpty;
 }
 
 /// Visitor for the struct/union cursor [CXCursorKind.CXCursor_StructDecl]/
@@ -245,7 +245,7 @@
       }
 
       final mt = cursor.type().toCodeGenType();
-      if (mt.broadType == BroadType.IncompleteArray) {
+      if (mt is IncompleteArray) {
         // TODO(68): Structs with flexible Array Members are not supported.
         parsed.flexibleArrayMember = true;
       }
@@ -253,13 +253,13 @@
         // TODO(84): Struct with bitfields are not suppoorted.
         parsed.bitFieldMember = true;
       }
-      if (mt.broadType == BroadType.Handle) {
+      if (mt is HandleType) {
         parsed.dartHandleMember = true;
       }
       if (mt.isIncompleteCompound) {
         parsed.incompleteCompoundMember = true;
       }
-      if (mt.getBaseType().broadType == BroadType.Unimplemented) {
+      if (mt.baseType is UnimplementedType) {
         parsed.unimplementedMemberType = true;
       }
 
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index 870cf47..b4c40fc 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -35,8 +35,8 @@
     final parameters = _getParameters(cursor, funcName);
 
     if (clang.clang_Cursor_isFunctionInlined(cursor) != 0) {
-      _logger.fine(
-          '---- Removed Function, reason: inline function: ${cursor.completeStringRepr()}');
+      _logger.fine('---- Removed Function, reason: inline function: '
+          '${cursor.completeStringRepr()}');
       _logger.warning(
           "Skipped Function '$funcName', inline functions are not supported.");
       // Returning null so that [addToBindings] function excludes this.
@@ -45,19 +45,22 @@
 
     if (rt.isIncompleteCompound || _stack.top.incompleteStructParameter) {
       _logger.fine(
-          '---- Removed Function, reason: Incomplete struct pass/return by value: ${cursor.completeStringRepr()}');
+          '---- Removed Function, reason: Incomplete struct pass/return by '
+          'value: ${cursor.completeStringRepr()}');
       _logger.warning(
-          "Skipped Function '$funcName', Incomplete struct pass/return by value not supported.");
+          "Skipped Function '$funcName', Incomplete struct pass/return by "
+          'value not supported.');
       // Returning null so that [addToBindings] function excludes this.
       return _stack.pop().func;
     }
 
-    if (rt.getBaseType().broadType == BroadType.Unimplemented ||
+    if (rt.baseType is UnimplementedType ||
         _stack.top.unimplementedParameterType) {
-      _logger.fine(
-          '---- Removed Function, reason: unsupported return type or parameter type: ${cursor.completeStringRepr()}');
+      _logger.fine('---- Removed Function, reason: unsupported return type or '
+          'parameter type: ${cursor.completeStringRepr()}');
       _logger.warning(
-          "Skipped Function '$funcName', function has unsupported return type or parameter type.");
+          "Skipped Function '$funcName', function has unsupported return type "
+          'or parameter type.');
       // Returning null so that [addToBindings] function excludes this.
       return _stack.pop().func;
     }
@@ -102,9 +105,8 @@
     final pt = _getParameterType(paramCursor);
     if (pt.isIncompleteCompound) {
       _stack.top.incompleteStructParameter = true;
-    } else if (pt.getBaseType().broadType == BroadType.Unimplemented) {
-      _logger
-          .finer('Unimplemented type: ${pt.getBaseType().unimplementedReason}');
+    } else if (pt.baseType is UnimplementedType) {
+      _logger.finer('Unimplemented type: ${pt.baseType}');
       _stack.top.unimplementedParameterType = true;
     }
 
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart
index 1905c53..41aae40 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart
@@ -41,11 +41,11 @@
   _logger.fine('++++ Adding ObjC interface: '
       'Name: $name, ${cursor.completeStringRepr()}');
 
-  return Type.objCInterface(ObjCInterface(
+  return ObjCInterface(
     usr: itfUsr, originalName: name,
     name: name, // TODO(#279): config.interfaceDecl.renameUsingConfig(name),
     dartDoc: getCursorDocComment(cursor),
-  ));
+  );
 }
 
 void fillObjCInterfaceMethodsIfNeeded(
@@ -88,7 +88,13 @@
   final superType = cursor.type().toCodeGenType();
   _logger.fine('       > Super type: '
       '$superType ${cursor.completeStringRepr()}');
-  _interfaceStack.top.interface.superType = superType.objCInterface;
+  final itf = _interfaceStack.top.interface;
+  if (superType is ObjCInterface) {
+    itf.superType = superType;
+  } else {
+    _logger.severe(
+        'Super type of $itf is $superType, which is not a valid interface.');
+  }
 }
 
 void _parseProperty(clang_types.CXCursor cursor) {
@@ -108,7 +114,7 @@
     dartDoc: dartDoc,
     kind: ObjCMethodKind.propertyGetter,
   );
-  getter.returnType = ObjCMethodType(fieldType);
+  getter.returnType = fieldType;
   itf.addMethod(getter);
 
   final setter = ObjCMethod(
@@ -119,7 +125,7 @@
     dartDoc: dartDoc,
     kind: ObjCMethodKind.propertySetter,
   );
-  setter.returnType = ObjCMethodType(Type.nativeType(SupportedNativeType.Void));
+  setter.returnType = NativeType(SupportedNativeType.Void);
   setter.params.add(ObjCMethodParam(fieldType, 'value'));
   itf.addMethod(setter);
 }
@@ -175,7 +181,7 @@
         '"${_interfaceStack.top.interface.originalName}" has multiple return '
         'types.');
   } else {
-    parsed.method.returnType = ObjCMethodType(cursor.type().toCodeGenType());
+    parsed.method.returnType = cursor.type().toCodeGenType();
     _logger.fine('           >> Return type: '
         '${parsed.method.returnType} ${cursor.completeStringRepr()}');
   }
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
index d8f84cf..988a45e 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
@@ -44,30 +44,28 @@
 
     if (bindingsIndex.isSeenUnsupportedTypealias(typedefUsr)) {
       // Do not process unsupported typealiases again.
-    } else if (s.broadType == BroadType.Unimplemented) {
-      _logger
-          .fine("Skipped Typedef '$typedefName': Unimplemented type referred.");
+    } else if (s is UnimplementedType) {
+      _logger.fine("Skipped Typedef '$typedefName': "
+          'Unimplemented type referred.');
       bindingsIndex.addUnsupportedTypealiasToSeen(typedefUsr);
-    } else if (s.broadType == BroadType.Compound &&
-        s.compound!.originalName == typedefName) {
+    } else if (s is Compound && s.originalName == typedefName) {
       // Ignore typedef if it refers to a compound with the same original name.
       bindingsIndex.addUnsupportedTypealiasToSeen(typedefUsr);
-      _logger.fine(
-          "Skipped Typedef '$typedefName': Name matches with referred struct/union.");
-    } else if (s.broadType == BroadType.Enum) {
+      _logger.fine("Skipped Typedef '$typedefName': "
+          'Name matches with referred struct/union.');
+    } else if (s is EnumClass) {
       // Ignore typedefs to Enum.
       bindingsIndex.addUnsupportedTypealiasToSeen(typedefUsr);
       _logger.fine("Skipped Typedef '$typedefName': typedef to enum.");
-    } else if (s.broadType == BroadType.Handle) {
+    } else if (s is HandleType) {
       // Ignore typedefs to Handle.
       _logger.fine("Skipped Typedef '$typedefName': typedef to Dart Handle.");
       bindingsIndex.addUnsupportedTypealiasToSeen(typedefUsr);
-    } else if (s.broadType == BroadType.ConstantArray ||
-        s.broadType == BroadType.IncompleteArray) {
+    } else if (s is ConstantArray || s is IncompleteArray) {
       // Ignore typedefs to Constant Array.
       _logger.fine("Skipped Typedef '$typedefName': typedef to array.");
       bindingsIndex.addUnsupportedTypealiasToSeen(typedefUsr);
-    } else if (s.broadType == BroadType.Boolean) {
+    } else if (s is BooleanType) {
       // Ignore typedefs to Boolean.
       _logger.fine("Skipped Typedef '$typedefName': typedef to bool.");
       bindingsIndex.addUnsupportedTypealiasToSeen(typedefUsr);
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart
index 9114f82..8376199 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/var_parser.dart
@@ -27,9 +27,9 @@
   _logger.fine('++++ Adding Global: ${cursor.completeStringRepr()}');
 
   final type = cursor.type().toCodeGenType();
-  if (type.getBaseType().broadType == BroadType.Unimplemented) {
-    _logger.fine(
-        '---- Removed Global, reason: unsupported type: ${cursor.completeStringRepr()}');
+  if (type.baseType is UnimplementedType) {
+    _logger.fine('---- Removed Global, reason: unsupported type: '
+        '${cursor.completeStringRepr()}');
     _logger.warning("Skipped global variable '$name', type not supported.");
     return null;
   }
diff --git a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
index 7778ddf..f643979 100644
--- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
@@ -46,10 +46,8 @@
           break;
         case clang_types.CXCursorKind.CXCursor_StructDecl:
         case clang_types.CXCursorKind.CXCursor_UnionDecl:
-          addToBindings(_getCodeGenTypeFromCursor(cursor)?.compound);
-          break;
         case clang_types.CXCursorKind.CXCursor_EnumDecl:
-          addToBindings(_getCodeGenTypeFromCursor(cursor)?.enumClass);
+          addToBindings(_getCodeGenTypeFromCursor(cursor));
           break;
         case clang_types.CXCursorKind.CXCursor_MacroDefinition:
           saveMacroDefinition(cursor);
@@ -58,7 +56,7 @@
           addToBindings(parseVarDeclaration(cursor));
           break;
         case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl:
-          addToBindings(_getCodeGenTypeFromCursor(cursor)?.objCInterface);
+          addToBindings(_getCodeGenTypeFromCursor(cursor));
           break;
         default:
           _logger.finer('rootCursorVisitor: CursorKind not implemented');
@@ -83,6 +81,7 @@
   }
 }
 
-Type? _getCodeGenTypeFromCursor(clang_types.CXCursor cursor) {
-  return getCodeGenType(cursor.type(), ignoreFilter: false);
+BindingType? _getCodeGenTypeFromCursor(clang_types.CXCursor cursor) {
+  final t = getCodeGenType(cursor.type(), ignoreFilter: false);
+  return t is BindingType ? t : null;
 }
diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
index 0f487a9..ae96a5a 100644
--- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
+++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -46,11 +46,11 @@
       case clang_types.CXTypeKind.CXType_ObjCObjectPointer:
       case clang_types.CXTypeKind.CXType_BlockPointer:
       case clang_types.CXTypeKind.CXType_ObjCId:
-      case clang_types.CXTypeKind.CXType_ObjCClass:
       case clang_types.CXTypeKind.CXType_ObjCTypeParam:
-        return Type.pointer(Type.struct(objCObjectType));
+      case clang_types.CXTypeKind.CXType_ObjCClass:
+        return PointerType(objCObjectType);
       case clang_types.CXTypeKind.CXType_ObjCSel:
-        return Type.pointer(Type.struct(objCSelType));
+        return PointerType(objCSelType);
     }
   }
 
@@ -65,8 +65,7 @@
           _createTypeFromCursor(cxtype, cursor, ignoreFilter, pointerReference);
       type = result.type;
       if (type == null) {
-        return Type.unimplemented(
-            'Type: ${cxtype.kindSpelling()} not implemented');
+        return UnimplementedType('${cxtype.kindSpelling()} not implemented');
       }
       if (result.addToCache) {
         bindingsIndex.addTypeToSeen(usr, type);
@@ -86,12 +85,12 @@
 
       // Replace Pointer<_Dart_Handle> with Handle.
       if (config.useDartHandle &&
-          s.broadType == BroadType.Compound &&
-          s.compound!.compoundType == CompoundType.struct &&
-          s.compound!.usr == strings.dartHandleUsr) {
-        return Type.handle();
+          s is Compound &&
+          s.compoundType == CompoundType.struct &&
+          s.usr == strings.dartHandleUsr) {
+        return HandleType();
       }
-      return Type.pointer(s);
+      return PointerType(s);
     case clang_types.CXTypeKind.CXType_FunctionProto:
       // Primarily used for function pointers.
       return _extractFromFunctionProto(cxtype);
@@ -100,17 +99,17 @@
       return _extractFromFunctionProto(cxtype);
     case clang_types.CXTypeKind
         .CXType_ConstantArray: // Primarily used for constant array in struct members.
-      return Type.constantArray(
+      return ConstantArray(
         clang.clang_getNumElements(cxtype),
         clang.clang_getArrayElementType(cxtype).toCodeGenType(),
       );
     case clang_types.CXTypeKind
         .CXType_IncompleteArray: // Primarily used for incomplete array in function parameters.
-      return Type.incompleteArray(
+      return IncompleteArray(
         clang.clang_getArrayElementType(cxtype).toCodeGenType(),
       );
     case clang_types.CXTypeKind.CXType_Bool:
-      return Type.boolean();
+      return BooleanType();
     default:
       var typeSpellKey =
           clang.clang_getTypeSpelling(cxtype).toStringAndDispose();
@@ -119,14 +118,13 @@
       }
       if (config.nativeTypeMappings.containsKey(typeSpellKey)) {
         _logger.fine('  Type $typeSpellKey mapped from type-map.');
-        return Type.importedType(config.nativeTypeMappings[typeSpellKey]!);
+        return config.nativeTypeMappings[typeSpellKey]!;
       } else if (cxTypeKindToImportedTypes.containsKey(typeSpellKey)) {
-        return Type.importedType(cxTypeKindToImportedTypes[typeSpellKey]!);
+        return cxTypeKindToImportedTypes[typeSpellKey]!;
       } else {
-        _logger.fine(
-            'typedeclarationCursorVisitor: getCodeGenType: Type Not Implemented, ${cxtype.completeStringRepr()}');
-        return Type.unimplemented(
-            'Type: ${cxtype.kindSpelling()} not implemented');
+        _logger.fine('typedeclarationCursorVisitor: getCodeGenType: Type Not '
+            'Implemented, ${cxtype.completeStringRepr()}');
+        return UnimplementedType('${cxtype.kindSpelling()} not implemented');
       }
   }
 }
@@ -153,18 +151,18 @@
       if (config.typedefTypeMappings.containsKey(spelling)) {
         _logger.fine('  Type $spelling mapped from type-map');
         return _CreateTypeFromCursorResult(
-            Type.importedType(config.typedefTypeMappings[spelling]!));
+            config.typedefTypeMappings[spelling]!);
       }
       // Get name from supported typedef name if config allows.
       if (config.useSupportedTypedefs) {
         if (suportedTypedefToSuportedNativeType.containsKey(spelling)) {
           _logger.fine('  Type Mapped from supported typedef');
           return _CreateTypeFromCursorResult(
-              Type.nativeType(suportedTypedefToSuportedNativeType[spelling]!));
+              NativeType(suportedTypedefToSuportedNativeType[spelling]!));
         } else if (supportedTypedefToImportedType.containsKey(spelling)) {
           _logger.fine('  Type Mapped from supported typedef');
           return _CreateTypeFromCursorResult(
-              Type.importedType(supportedTypedefToImportedType[spelling]!));
+              supportedTypedefToImportedType[spelling]!);
         }
       }
 
@@ -172,7 +170,7 @@
           parseTypedefDeclaration(cursor, pointerReference: pointerReference);
 
       if (typealias != null) {
-        return _CreateTypeFromCursorResult(Type.typealias(typealias));
+        return _CreateTypeFromCursorResult(typealias);
       } else {
         // Use underlying type if typealias couldn't be created or if the user
         // excluded this typedef.
@@ -191,10 +189,10 @@
       );
       if (enumClass == null) {
         // Handle anonymous enum declarations within another declaration.
-        return _CreateTypeFromCursorResult(Type.nativeType(Type.enumNativeType),
+        return _CreateTypeFromCursorResult(EnumClass.nativeType,
             addToCache: false);
       } else {
-        return _CreateTypeFromCursorResult(Type.enumClass(enumClass));
+        return _CreateTypeFromCursorResult(enumClass);
       }
     case clang_types.CXTypeKind.CXType_ObjCInterface:
       return _CreateTypeFromCursorResult(parseObjCInterfaceDeclaration(cursor));
@@ -206,11 +204,11 @@
 void _fillFromCursorIfNeeded(Type? type, clang_types.CXCursor cursor,
     bool ignoreFilter, bool pointerReference) {
   if (type == null) return;
-  if (type.compound != null) {
-    fillCompoundMembersIfNeeded(type.compound!, cursor,
+  if (type is Compound) {
+    fillCompoundMembersIfNeeded(type, cursor,
         ignoreFilter: ignoreFilter, pointerReference: pointerReference);
-  } else if (type.objCInterface != null) {
-    fillObjCInterfaceMethodsIfNeeded(type.objCInterface!, cursor);
+  } else if (type is ObjCInterface) {
+    fillObjCInterfaceMethodsIfNeeded(type, cursor);
   }
 }
 
@@ -244,7 +242,7 @@
     // TODO(23): Check if we should auto add compound declarations.
     if (compoundTypeMappings.containsKey(declSpelling)) {
       _logger.fine('  Type Mapped from type-map');
-      return Type.importedType(compoundTypeMappings[declSpelling]!);
+      return compoundTypeMappings[declSpelling]!;
     } else {
       final struct = parseCompoundDeclaration(
         cursor,
@@ -252,13 +250,12 @@
         ignoreFilter: ignoreFilter,
         pointerReference: pointerReference,
       );
-      if (struct == null) return null;
-      return Type.compound(struct);
+      return struct;
     }
   }
-  _logger.fine(
-      'typedeclarationCursorVisitor: _extractfromRecord: Not Implemented, ${cursor.completeStringRepr()}');
-  return Type.unimplemented('Type: ${cxtype.kindSpelling()} not implemented');
+  _logger.fine('typedeclarationCursorVisitor: _extractfromRecord: '
+      'Not Implemented, ${cursor.completeStringRepr()}');
+  return UnimplementedType('${cxtype.kindSpelling()} not implemented');
 }
 
 // Used for function pointer arguments.
@@ -270,10 +267,10 @@
     final pt = t.toCodeGenType();
 
     if (pt.isIncompleteCompound) {
-      return Type.unimplemented(
+      return UnimplementedType(
           'Incomplete Struct by value in function parameter.');
-    } else if (pt.getBaseType().broadType == BroadType.Unimplemented) {
-      return Type.unimplemented('Function parameter has an unsupported type.');
+    } else if (pt.baseType is UnimplementedType) {
+      return UnimplementedType('Function parameter has an unsupported type.');
     }
 
     _parameters.add(
@@ -281,8 +278,8 @@
     );
   }
 
-  return Type.nativeFunc(NativeFunc.fromFunctionType(FunctionType(
+  return NativeFunc(FunctionType(
     parameters: _parameters,
     returnType: clang.clang_getResultType(cxtype).toCodeGenType(),
-  )));
+  ));
 }
diff --git a/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart b/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart
index 9178cf0..5ef4980 100644
--- a/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart
+++ b/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart
@@ -15,7 +15,7 @@
           Func(
             name: 'noParam',
             dartDoc: 'Just a test function\nheres another line',
-            returnType: Type.nativeType(
+            returnType: NativeType(
               SupportedNativeType.Int32,
             ),
           ),
@@ -24,18 +24,18 @@
             parameters: [
               Parameter(
                 name: 'a',
-                type: Type.nativeType(
+                type: NativeType(
                   SupportedNativeType.Int32,
                 ),
               ),
               Parameter(
                 name: 'b',
-                type: Type.nativeType(
+                type: NativeType(
                   SupportedNativeType.Uint8,
                 ),
               ),
             ],
-            returnType: Type.nativeType(
+            returnType: NativeType(
               SupportedNativeType.Char,
             ),
           ),
@@ -44,25 +44,25 @@
             parameters: [
               Parameter(
                 name: 'a',
-                type: Type.pointer(
-                  Type.nativeType(
+                type: PointerType(
+                  NativeType(
                     SupportedNativeType.Int32,
                   ),
                 ),
               ),
               Parameter(
                 name: 'b',
-                type: Type.pointer(
-                  Type.pointer(
-                    Type.nativeType(
+                type: PointerType(
+                  PointerType(
+                    NativeType(
                       SupportedNativeType.Uint8,
                     ),
                   ),
                 ),
               ),
             ],
-            returnType: Type.pointer(
-              Type.nativeType(
+            returnType: PointerType(
+              NativeType(
                 SupportedNativeType.Double,
               ),
             ),
@@ -74,12 +74,12 @@
             parameters: [
               Parameter(
                 name: 'a',
-                type: Type.nativeType(
+                type: NativeType(
                   SupportedNativeType.Int32,
                 ),
               ),
             ],
-            returnType: Type.nativeType(
+            returnType: NativeType(
               SupportedNativeType.Int32,
             ),
           ),
@@ -93,49 +93,49 @@
       final library = Library(
         name: 'Bindings',
         bindings: [
-          Struc(
+          Struct(
             name: 'NoMember',
             dartDoc: 'Just a test struct\nheres another line',
           ),
-          Struc(
+          Struct(
             name: 'WithPrimitiveMember',
             members: [
               Member(
                 name: 'a',
-                type: Type.nativeType(
+                type: NativeType(
                   SupportedNativeType.Int32,
                 ),
               ),
               Member(
                 name: 'b',
-                type: Type.nativeType(
+                type: NativeType(
                   SupportedNativeType.Double,
                 ),
               ),
               Member(
                 name: 'c',
-                type: Type.nativeType(
+                type: NativeType(
                   SupportedNativeType.Char,
                 ),
               ),
             ],
           ),
-          Struc(
+          Struct(
             name: 'WithPointerMember',
             members: [
               Member(
                 name: 'a',
-                type: Type.pointer(
-                  Type.nativeType(
+                type: PointerType(
+                  NativeType(
                     SupportedNativeType.Int32,
                   ),
                 ),
               ),
               Member(
                 name: 'b',
-                type: Type.pointer(
-                  Type.pointer(
-                    Type.nativeType(
+                type: PointerType(
+                  PointerType(
+                    NativeType(
                       SupportedNativeType.Double,
                     ),
                   ),
@@ -143,7 +143,7 @@
               ),
               Member(
                 name: 'c',
-                type: Type.nativeType(
+                type: NativeType(
                   SupportedNativeType.Char,
                 ),
               ),
@@ -156,24 +156,24 @@
     });
 
     test('Function and Struct Binding (pointer to Struct)', () {
-      final structSome = Struc(
-        name: 'SomeStruc',
+      final structSome = Struct(
+        name: 'SomeStruct',
         members: [
           Member(
             name: 'a',
-            type: Type.nativeType(
+            type: NativeType(
               SupportedNativeType.Int32,
             ),
           ),
           Member(
             name: 'b',
-            type: Type.nativeType(
+            type: NativeType(
               SupportedNativeType.Double,
             ),
           ),
           Member(
             name: 'c',
-            type: Type.nativeType(
+            type: NativeType(
               SupportedNativeType.Char,
             ),
           ),
@@ -188,19 +188,15 @@
             parameters: [
               Parameter(
                 name: 'some',
-                type: Type.pointer(
-                  Type.pointer(
-                    Type.struct(
-                      structSome,
-                    ),
+                type: PointerType(
+                  PointerType(
+                    structSome,
                   ),
                 ),
               ),
             ],
-            returnType: Type.pointer(
-              Type.struct(
-                structSome,
-              ),
+            returnType: PointerType(
+              structSome,
             ),
           ),
         ],
@@ -210,39 +206,37 @@
     });
 
     test('global (primitives, pointers, pointer to struct)', () {
-      final strucSome = Struc(
+      final structSome = Struct(
         name: 'Some',
       );
-      final emptyGlobalStruc = Struc(name: 'EmptyStruct');
+      final emptyGlobalStruct = Struct(name: 'EmptyStruct');
 
       final library = Library(
         name: 'Bindings',
         bindings: [
           Global(
             name: 'test1',
-            type: Type.nativeType(
+            type: NativeType(
               SupportedNativeType.Int32,
             ),
           ),
           Global(
             name: 'test2',
-            type: Type.pointer(
-              Type.nativeType(
+            type: PointerType(
+              NativeType(
                 SupportedNativeType.Float,
               ),
             ),
           ),
-          strucSome,
+          structSome,
           Global(
             name: 'test5',
-            type: Type.pointer(
-              Type.struct(
-                strucSome,
-              ),
+            type: PointerType(
+              structSome,
             ),
           ),
-          emptyGlobalStruc,
-          Global(name: 'globalStruct', type: Type.struct(emptyGlobalStruc)),
+          emptyGlobalStruct,
+          Global(name: 'globalStruct', type: emptyGlobalStruct),
         ],
       );
       _matchLib(library, 'global');
@@ -296,38 +290,38 @@
         bindings: [
           Func(
             name: 'test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
+            returnType: NativeType(SupportedNativeType.Void),
           ),
           Func(
             name: '_test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
+            returnType: NativeType(SupportedNativeType.Void),
           ),
           Func(
             name: '_c_test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
+            returnType: NativeType(SupportedNativeType.Void),
           ),
           Func(
             name: '_dart_test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
+            returnType: NativeType(SupportedNativeType.Void),
           ),
-          Struc(
+          Struct(
             name: '_Test',
             members: [
               Member(
                 name: 'array',
-                type: Type.constantArray(
+                type: ConstantArray(
                   2,
-                  Type.nativeType(
+                  NativeType(
                     SupportedNativeType.Int8,
                   ),
                 ),
               ),
             ],
           ),
-          Struc(name: 'ArrayHelperPrefixCollisionTest'),
+          Struct(name: 'ArrayHelperPrefixCollisionTest'),
           Func(
             name: 'Test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
+            returnType: NativeType(SupportedNativeType.Void),
           ),
           EnumClass(name: '_c_Test'),
           EnumClass(name: 'init_dylib'),
@@ -343,16 +337,16 @@
       bindings: [
         Func(
           name: 'test1',
-          returnType: Type.boolean(),
+          returnType: BooleanType(),
           parameters: [
-            Parameter(name: 'a', type: Type.boolean()),
-            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
+            Parameter(name: 'a', type: BooleanType()),
+            Parameter(name: 'b', type: PointerType(BooleanType())),
           ],
         ),
-        Struc(
+        Struct(
           name: 'Test2',
           members: [
-            Member(name: 'a', type: Type.boolean()),
+            Member(name: 'a', type: BooleanType()),
           ],
         ),
       ],
@@ -366,16 +360,16 @@
       bindings: [
         Func(
           name: 'test1',
-          returnType: Type.boolean(),
+          returnType: BooleanType(),
           parameters: [
-            Parameter(name: 'a', type: Type.boolean()),
-            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
+            Parameter(name: 'a', type: BooleanType()),
+            Parameter(name: 'b', type: PointerType(BooleanType())),
           ],
         ),
-        Struc(
+        Struct(
           name: 'Test2',
           members: [
-            Member(name: 'a', type: Type.boolean()),
+            Member(name: 'a', type: BooleanType()),
           ],
         ),
       ],
@@ -387,10 +381,10 @@
       name: 'Bindings',
       sort: true,
       bindings: [
-        Func(name: 'b', returnType: Type.nativeType(SupportedNativeType.Void)),
-        Func(name: 'a', returnType: Type.nativeType(SupportedNativeType.Void)),
-        Struc(name: 'D'),
-        Struc(name: 'C'),
+        Func(name: 'b', returnType: NativeType(SupportedNativeType.Void)),
+        Func(name: 'a', returnType: NativeType(SupportedNativeType.Void)),
+        Struct(name: 'D'),
+        Struct(name: 'C'),
       ],
     );
     _matchLib(library, 'sort_bindings');
@@ -399,35 +393,33 @@
     final library = Library(
       name: 'Bindings',
       bindings: [
-        Struc(name: 'NoPacking', pack: null, members: [
-          Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+        Struct(name: 'NoPacking', pack: null, members: [
+          Member(name: 'a', type: NativeType(SupportedNativeType.Char)),
         ]),
-        Struc(name: 'Pack1', pack: 1, members: [
-          Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+        Struct(name: 'Pack1', pack: 1, members: [
+          Member(name: 'a', type: NativeType(SupportedNativeType.Char)),
         ]),
-        Struc(name: 'Pack2', pack: 2, members: [
-          Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+        Struct(name: 'Pack2', pack: 2, members: [
+          Member(name: 'a', type: NativeType(SupportedNativeType.Char)),
         ]),
-        Struc(name: 'Pack2', pack: 4, members: [
-          Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+        Struct(name: 'Pack2', pack: 4, members: [
+          Member(name: 'a', type: NativeType(SupportedNativeType.Char)),
         ]),
-        Struc(name: 'Pack2', pack: 8, members: [
-          Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+        Struct(name: 'Pack2', pack: 8, members: [
+          Member(name: 'a', type: NativeType(SupportedNativeType.Char)),
         ]),
-        Struc(name: 'Pack16', pack: 16, members: [
-          Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+        Struct(name: 'Pack16', pack: 16, members: [
+          Member(name: 'a', type: NativeType(SupportedNativeType.Char)),
         ]),
       ],
     );
     _matchLib(library, 'packed_structs');
   });
   test('Union Bindings', () {
-    final struct1 = Struc(
-        name: 'Struct1',
-        members: [Member(name: 'a', type: Type.importedType(charType))]);
-    final union1 = Union(
-        name: 'Union1',
-        members: [Member(name: 'a', type: Type.importedType(charType))]);
+    final struct1 =
+        Struct(name: 'Struct1', members: [Member(name: 'a', type: charType)]);
+    final union1 =
+        Union(name: 'Union1', members: [Member(name: 'a', type: charType)]);
     final library = Library(
       name: 'Bindings',
       bindings: [
@@ -435,27 +427,23 @@
         union1,
         Union(name: 'EmptyUnion'),
         Union(name: 'Primitives', members: [
-          Member(name: 'a', type: Type.importedType(charType)),
-          Member(name: 'b', type: Type.importedType(intType)),
-          Member(name: 'c', type: Type.importedType(floatType)),
-          Member(name: 'd', type: Type.importedType(doubleType)),
+          Member(name: 'a', type: charType),
+          Member(name: 'b', type: intType),
+          Member(name: 'c', type: floatType),
+          Member(name: 'd', type: doubleType),
         ]),
         Union(name: 'PrimitivesWithPointers', members: [
-          Member(name: 'a', type: Type.importedType(charType)),
-          Member(name: 'b', type: Type.importedType(floatType)),
-          Member(name: 'c', type: Type.pointer(Type.importedType(doubleType))),
-          Member(name: 'd', type: Type.pointer(Type.union(union1))),
-          Member(name: 'd', type: Type.pointer(Type.struct(struct1))),
+          Member(name: 'a', type: charType),
+          Member(name: 'b', type: floatType),
+          Member(name: 'c', type: PointerType(doubleType)),
+          Member(name: 'd', type: PointerType(union1)),
+          Member(name: 'd', type: PointerType(struct1)),
         ]),
         Union(name: 'WithArray', members: [
-          Member(
-              name: 'a',
-              type: Type.constantArray(10, Type.importedType(charType))),
-          Member(name: 'b', type: Type.constantArray(10, Type.union(union1))),
-          Member(name: 'b', type: Type.constantArray(10, Type.struct(struct1))),
-          Member(
-              name: 'c',
-              type: Type.constantArray(10, Type.pointer(Type.union(union1)))),
+          Member(name: 'a', type: ConstantArray(10, charType)),
+          Member(name: 'b', type: ConstantArray(10, union1)),
+          Member(name: 'b', type: ConstantArray(10, struct1)),
+          Member(name: 'c', type: ConstantArray(10, PointerType(union1))),
         ]),
       ],
     );
@@ -466,29 +454,26 @@
       name: 'Bindings',
       header: '// ignore_for_file: non_constant_identifier_names\n',
       bindings: [
-        Typealias(
-            name: 'RawUnused', type: Type.compound(Struc(name: 'Struct1'))),
-        Struc(name: 'WithTypealiasStruc', members: [
+        Typealias(name: 'RawUnused', type: Struct(name: 'Struct1')),
+        Struct(name: 'WithTypealiasStruct', members: [
           Member(
               name: 't',
-              type: Type.typealias(Typealias(
+              type: Typealias(
                   name: 'Struct2Typealias',
-                  type: Type.struct(Struc(name: 'Struct2', members: [
-                    Member(name: 'a', type: Type.importedType(doubleType))
-                  ])))))
+                  type: Struct(
+                      name: 'Struct2',
+                      members: [Member(name: 'a', type: doubleType)])))
         ]),
         Func(
-            name: 'WithTypealiasStruc',
-            returnType: Type.pointer(Type.nativeFunc(
-                NativeFunc.fromFunctionType(FunctionType(
-                    returnType: Type.nativeType(SupportedNativeType.Void),
-                    parameters: [])))),
+            name: 'WithTypealiasStruct',
+            returnType: PointerType(NativeFunc(FunctionType(
+                returnType: NativeType(SupportedNativeType.Void),
+                parameters: []))),
             parameters: [
               Parameter(
                   name: 't',
-                  type: Type.typealias(Typealias(
-                      name: 'Struct3Typealias',
-                      type: Type.struct(Struc(name: 'Struct3')))))
+                  type: Typealias(
+                      name: 'Struct3Typealias', type: Struct(name: 'Struct3')))
             ]),
       ],
     );
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
index df895e3..73a44cc 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
@@ -18,8 +18,8 @@
           lookup)
       : _lookup = lookup;
 
-  ffi.Pointer<SomeStruc> someFunc(
-    ffi.Pointer<ffi.Pointer<SomeStruc>> some,
+  ffi.Pointer<SomeStruct> someFunc(
+    ffi.Pointer<ffi.Pointer<SomeStruct>> some,
   ) {
     return _someFunc(
       some,
@@ -28,13 +28,13 @@
 
   late final _someFuncPtr = _lookup<
       ffi.NativeFunction<
-          ffi.Pointer<SomeStruc> Function(
-              ffi.Pointer<ffi.Pointer<SomeStruc>>)>>('someFunc');
+          ffi.Pointer<SomeStruct> Function(
+              ffi.Pointer<ffi.Pointer<SomeStruct>>)>>('someFunc');
   late final _someFunc = _someFuncPtr.asFunction<
-      ffi.Pointer<SomeStruc> Function(ffi.Pointer<ffi.Pointer<SomeStruc>>)>();
+      ffi.Pointer<SomeStruct> Function(ffi.Pointer<ffi.Pointer<SomeStruct>>)>();
 }
 
-class SomeStruc extends ffi.Struct {
+class SomeStruct extends ffi.Struct {
   @ffi.Int32()
   external int a;
 
diff --git a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart
index e0c4884..206a36c 100644
--- a/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart
+++ b/pkgs/ffigen/test/code_generator_tests/expected_bindings/_expected_typealias_bindings.dart
@@ -20,19 +20,19 @@
           lookup)
       : _lookup = lookup;
 
-  ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> WithTypealiasStruc1(
+  ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> WithTypealiasStruct1(
     Struct3Typealias t,
   ) {
-    return _WithTypealiasStruc1(
+    return _WithTypealiasStruct1(
       t,
     );
   }
 
-  late final _WithTypealiasStruc1Ptr = _lookup<
+  late final _WithTypealiasStruct1Ptr = _lookup<
       ffi.NativeFunction<
           ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> Function(
-              Struct3Typealias)>>('WithTypealiasStruc');
-  late final _WithTypealiasStruc1 = _WithTypealiasStruc1Ptr.asFunction<
+              Struct3Typealias)>>('WithTypealiasStruct');
+  late final _WithTypealiasStruct1 = _WithTypealiasStruct1Ptr.asFunction<
       ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> Function(
           Struct3Typealias)>();
 }
@@ -41,7 +41,7 @@
 
 class Struct1 extends ffi.Opaque {}
 
-class WithTypealiasStruc extends ffi.Struct {
+class WithTypealiasStruct extends ffi.Struct {
   external Struct2Typealias t;
 }
 
diff --git a/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart b/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart
index 914563b..4529f29 100644
--- a/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart
+++ b/pkgs/ffigen/test/collision_tests/decl_decl_collision_test.dart
@@ -14,16 +14,14 @@
     });
     test('declaration conflict', () {
       final l1 = Library(name: 'Bindings', bindings: [
-        Struc(name: 'TestStruc'),
-        Struc(name: 'TestStruc'),
+        Struct(name: 'TestStruct'),
+        Struct(name: 'TestStruct'),
         EnumClass(name: 'TestEnum'),
         EnumClass(name: 'TestEnum'),
         Func(
-            name: 'testFunc',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            name: 'testFunc', returnType: NativeType(SupportedNativeType.Void)),
         Func(
-            name: 'testFunc',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            name: 'testFunc', returnType: NativeType(SupportedNativeType.Void)),
         Constant(
           originalName: 'Test_Macro',
           name: 'Test_Macro',
@@ -37,40 +35,37 @@
           rawValue: '0',
         ),
         Typealias(
-            name: 'testAlias', type: Type.nativeType(SupportedNativeType.Void)),
+            name: 'testAlias', type: NativeType(SupportedNativeType.Void)),
         Typealias(
-            name: 'testAlias', type: Type.nativeType(SupportedNativeType.Void)),
+            name: 'testAlias', type: NativeType(SupportedNativeType.Void)),
 
         /// Conflicts across declarations.
-        Struc(name: 'testCrossDecl'),
+        Struct(name: 'testCrossDecl'),
         Func(
             name: 'testCrossDecl',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            returnType: NativeType(SupportedNativeType.Void)),
         Constant(name: 'testCrossDecl', rawValue: '0', rawType: 'int'),
         EnumClass(name: 'testCrossDecl'),
         Typealias(
-            name: 'testCrossDecl',
-            type: Type.nativeType(SupportedNativeType.Void)),
+            name: 'testCrossDecl', type: NativeType(SupportedNativeType.Void)),
 
         /// Conflicts with ffi library prefix, name of prefix is changed.
-        Struc(name: 'ffi'),
-        Func(
-            name: 'ffi1',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+        Struct(name: 'ffi'),
+        Func(name: 'ffi1', returnType: NativeType(SupportedNativeType.Void)),
       ]);
       final l2 = Library(name: 'Bindings', bindings: [
-        Struc(name: 'TestStruc'),
-        Struc(name: 'TestStruc1'),
+        Struct(name: 'TestStruct'),
+        Struct(name: 'TestStruct1'),
         EnumClass(name: 'TestEnum'),
         EnumClass(name: 'TestEnum1'),
         Func(
             name: 'testFunc',
             originalName: 'testFunc',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            returnType: NativeType(SupportedNativeType.Void)),
         Func(
             name: 'testFunc1',
             originalName: 'testFunc',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            returnType: NativeType(SupportedNativeType.Void)),
         Constant(
           originalName: 'Test_Macro',
           name: 'Test_Macro',
@@ -84,24 +79,20 @@
           rawValue: '0',
         ),
         Typealias(
-            name: 'testAlias', type: Type.nativeType(SupportedNativeType.Void)),
+            name: 'testAlias', type: NativeType(SupportedNativeType.Void)),
         Typealias(
-            name: 'testAlias1',
-            type: Type.nativeType(SupportedNativeType.Void)),
-        Struc(name: 'testCrossDecl', originalName: 'testCrossDecl'),
+            name: 'testAlias1', type: NativeType(SupportedNativeType.Void)),
+        Struct(name: 'testCrossDecl', originalName: 'testCrossDecl'),
         Func(
             name: 'testCrossDecl1',
             originalName: 'testCrossDecl',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            returnType: NativeType(SupportedNativeType.Void)),
         Constant(name: 'testCrossDecl2', rawValue: '0', rawType: 'int'),
         EnumClass(name: 'testCrossDecl3'),
         Typealias(
-            name: 'testCrossDecl4',
-            type: Type.nativeType(SupportedNativeType.Void)),
-        Struc(name: 'ffi'),
-        Func(
-            name: 'ffi1',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            name: 'testCrossDecl4', type: NativeType(SupportedNativeType.Void)),
+        Struct(name: 'ffi'),
+        Func(name: 'ffi1', returnType: NativeType(SupportedNativeType.Void)),
       ]);
 
       expect(l1.generate(), l2.generate());
diff --git a/pkgs/ffigen/test/collision_tests/decl_symbol_address_collision_test.dart b/pkgs/ffigen/test/collision_tests/decl_symbol_address_collision_test.dart
index 9520139..1d2744b 100644
--- a/pkgs/ffigen/test/collision_tests/decl_symbol_address_collision_test.dart
+++ b/pkgs/ffigen/test/collision_tests/decl_symbol_address_collision_test.dart
@@ -17,18 +17,18 @@
         header:
             '// ignore_for_file: unused_element, camel_case_types, non_constant_identifier_names\n',
         bindings: [
-          Struc(name: 'addresses'),
-          Struc(name: '_SymbolAddresses'),
+          Struct(name: 'addresses'),
+          Struct(name: '_SymbolAddresses'),
           EnumClass(name: 'Bindings'),
           Func(
             name: '_library',
-            returnType: Type.nativeType(SupportedNativeType.Void),
+            returnType: NativeType(SupportedNativeType.Void),
             exposeSymbolAddress: true,
             exposeFunctionTypedefs: true,
           ),
           Func(
             name: '_SymbolAddresses_1',
-            returnType: Type.nativeType(SupportedNativeType.Void),
+            returnType: NativeType(SupportedNativeType.Void),
             exposeSymbolAddress: true,
           ),
         ],
diff --git a/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart b/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart
index eb2e61c..c6873f2 100644
--- a/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart
+++ b/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart
@@ -15,73 +15,70 @@
     });
     test('reserved keyword collision', () {
       final l1 = Library(name: 'Bindings', bindings: [
-        Struc(name: 'abstract'),
-        Struc(name: 'abstract'),
-        Struc(name: 'if'),
+        Struct(name: 'abstract'),
+        Struct(name: 'abstract'),
+        Struct(name: 'if'),
         EnumClass(name: 'return'),
         EnumClass(name: 'export'),
-        Func(
-            name: 'show',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+        Func(name: 'show', returnType: NativeType(SupportedNativeType.Void)),
         Func(
             name: 'implements',
             parameters: [
               Parameter(
-                type: Type.importedType(intType),
+                type: intType,
                 name: 'if',
               ),
               Parameter(
-                type: Type.importedType(intType),
+                type: intType,
                 name: 'abstract',
               ),
               Parameter(
-                type: Type.importedType(intType),
+                type: intType,
                 name: 'in',
               ),
             ],
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            returnType: NativeType(SupportedNativeType.Void)),
         Constant(
           name: 'else',
           rawType: 'int',
           rawValue: '0',
         ),
-        Typealias(name: 'var', type: Type.nativeType(SupportedNativeType.Void)),
+        Typealias(name: 'var', type: NativeType(SupportedNativeType.Void)),
       ]);
       final l2 = Library(name: 'Bindings', bindings: [
-        Struc(name: 'abstract1'),
-        Struc(name: 'abstract2'),
-        Struc(name: 'if1'),
+        Struct(name: 'abstract1'),
+        Struct(name: 'abstract2'),
+        Struct(name: 'if1'),
         EnumClass(name: 'return1'),
         EnumClass(name: 'export1'),
         Func(
             name: 'show1',
             originalName: 'show',
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            returnType: NativeType(SupportedNativeType.Void)),
         Func(
             name: 'implements1',
             originalName: 'implements',
             parameters: [
               Parameter(
-                type: Type.importedType(intType),
+                type: intType,
                 name: 'if1',
               ),
               Parameter(
-                type: Type.importedType(intType),
+                type: intType,
                 name: 'abstract1',
               ),
               Parameter(
-                type: Type.importedType(intType),
+                type: intType,
                 name: 'in1',
               ),
             ],
-            returnType: Type.nativeType(SupportedNativeType.Void)),
+            returnType: NativeType(SupportedNativeType.Void)),
         Constant(
           name: 'else1',
           rawType: 'int',
           rawValue: '0',
         ),
-        Typealias(
-            name: 'var1', type: Type.nativeType(SupportedNativeType.Void)),
+        Typealias(name: 'var1', type: NativeType(SupportedNativeType.Void)),
       ]);
       expect(l1.generate(), l2.generate());
     });
diff --git a/pkgs/ffigen/test/config_tests/packed_struct_override_test.dart b/pkgs/ffigen/test/config_tests/packed_struct_override_test.dart
index ca371d8..9f9726d 100644
--- a/pkgs/ffigen/test/config_tests/packed_struct_override_test.dart
+++ b/pkgs/ffigen/test/config_tests/packed_struct_override_test.dart
@@ -54,9 +54,9 @@
 
       final library = parse(config);
 
-      expect((library.getBinding('NormalStruct1') as Struc).pack, 1);
-      expect((library.getBinding('StructWithAttr') as Struc).pack, 2);
-      expect((library.getBinding('PackedAttr') as Struc).pack, null);
+      expect((library.getBinding('NormalStruct1') as Struct).pack, 1);
+      expect((library.getBinding('StructWithAttr') as Struct).pack, 2);
+      expect((library.getBinding('PackedAttr') as Struct).pack, null);
     });
   });
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/dart_handle.h b/pkgs/ffigen/test/header_parser_tests/dart_handle.h
index 8a2d802..26d73fa 100644
--- a/pkgs/ffigen/test/header_parser_tests/dart_handle.h
+++ b/pkgs/ffigen/test/header_parser_tests/dart_handle.h
@@ -12,14 +12,14 @@
 void func4(Typedef1);
 
 // Dart_Handle isn't supported directly, so all members are removed.
-struct Struc1
+struct Struct1
 {
     Dart_Handle h;
     int a;
 };
 
 // Pointer<Handle> works.
-struct Struc2
+struct Struct2
 {
     Dart_Handle *h;
 };
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
index 14b429c..e9ebee7 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart
@@ -71,8 +71,8 @@
 typedef Typedef1
     = ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Handle)>>;
 
-class Struc1 extends ffi.Opaque {}
+class Struct1 extends ffi.Opaque {}
 
-class Struc2 extends ffi.Struct {
+class Struct2 extends ffi.Struct {
   external ffi.Pointer<ffi.Handle> h;
 }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
index baf9c6e..a980d68 100644
--- a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
+++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart
@@ -64,7 +64,7 @@
       _funcWithNativeFuncPtr.asFunction<void Function(WithTypedefReturnType)>();
 }
 
-class Struc extends ffi.Struct {
+class Struct extends ffi.Struct {
   external ffi.Pointer<
       ffi.NativeFunction<
           ffi.Void Function(
@@ -75,7 +75,7 @@
     = ffi.Pointer<ffi.NativeFunction<InsideReturnType Function()>>;
 typedef InsideReturnType = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
 
-class Struc2 extends ffi.Struct {
+class Struct2 extends ffi.Struct {
   external VoidFuncPointer constFuncPointer;
 }
 
diff --git a/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart b/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart
index d56cd4e..98dfafe 100644
--- a/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart
@@ -48,13 +48,13 @@
           expected.getBindingAsString('Struct2'));
     });
     test('Struct3 flexible array member', () {
-      expect((actual.getBinding('Struct3') as Struc).members.isEmpty, true);
+      expect((actual.getBinding('Struct3') as Struct).members.isEmpty, true);
     });
     test('Struct4 bit field member', () {
-      expect((actual.getBinding('Struct4') as Struc).members.isEmpty, true);
+      expect((actual.getBinding('Struct4') as Struct).members.isEmpty, true);
     });
     test('Struct5 incompleted struct member', () {
-      expect((actual.getBinding('Struct5') as Struc).members.isEmpty, true);
+      expect((actual.getBinding('Struct5') as Struct).members.isEmpty, true);
     });
     test('Struct6 typedef constant array', () {
       expect(actual.getBindingAsString('Struct6'),
@@ -68,59 +68,56 @@
 }
 
 Library expectedLibrary() {
-  final struc1 = Struc(name: 'Struct1', members: [
+  final struct1 = Struct(name: 'Struct1', members: [
     Member(
       name: 'a',
-      type: Type.importedType(intType),
+      type: intType,
     ),
   ]);
-  final struc2 = Struc(name: 'Struct2', members: [
+  final struct2 = Struct(name: 'Struct2', members: [
     Member(
       name: 'a',
-      type: Type.struct(struc1),
+      type: struct1,
     ),
   ]);
-  final struc3 = Struc(name: 'Struct3');
+  final struct3 = Struct(name: 'Struct3');
   return Library(
     name: 'Bindings',
     bindings: [
-      struc1,
-      struc2,
-      struc3,
+      struct1,
+      struct2,
+      struct3,
       Func(
         name: 'func1',
         parameters: [
-          Parameter(name: 's', type: Type.pointer(Type.struct(struc2))),
+          Parameter(name: 's', type: PointerType(struct2)),
         ],
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
       ),
       Func(
         name: 'func2',
         parameters: [
-          Parameter(name: 's', type: Type.pointer(Type.struct(struc3))),
+          Parameter(name: 's', type: PointerType(struct3)),
         ],
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
       ),
       Func(
         name: 'func3',
         parameters: [
-          Parameter(name: 'a', type: Type.pointer(Type.importedType(intType))),
+          Parameter(name: 'a', type: PointerType(intType)),
         ],
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
       ),
-      Struc(name: 'Struct4'),
-      Struc(name: 'Struct5'),
-      Struc(name: 'Struct6', members: [
-        Member(
-            name: 'a',
-            type: Type.constantArray(
-                2, Type.constantArray(10, Type.importedType(intType))))
+      Struct(name: 'Struct4'),
+      Struct(name: 'Struct5'),
+      Struct(name: 'Struct6', members: [
+        Member(name: 'a', type: ConstantArray(2, ConstantArray(10, intType)))
       ]),
     ],
   );
diff --git a/pkgs/ffigen/test/header_parser_tests/globals_test.dart b/pkgs/ffigen/test/header_parser_tests/globals_test.dart
index be10f3c..1872ced 100644
--- a/pkgs/ffigen/test/header_parser_tests/globals_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/globals_test.dart
@@ -66,34 +66,32 @@
 }
 
 Library expectedLibrary() {
-  final globalStruc = Struc(name: 'EmptyStruct');
+  final globalStruct = Struct(name: 'EmptyStruct');
   return Library(
     name: 'Bindings',
     bindings: [
-      Global(type: Type.boolean(), name: 'coolGlobal'),
+      Global(type: BooleanType(), name: 'coolGlobal'),
       Global(
-        type: Type.nativeType(SupportedNativeType.Int32),
+        type: NativeType(SupportedNativeType.Int32),
         name: 'myInt',
         exposeSymbolAddress: true,
       ),
       Global(
-        type: Type.pointer(Type.nativeType(SupportedNativeType.Int32)),
+        type: PointerType(NativeType(SupportedNativeType.Int32)),
         name: 'aGlobalPointer',
         exposeSymbolAddress: true,
       ),
-      globalStruc,
+      globalStruct,
       Global(
         name: 'globalStruct',
-        type: Type.struct(globalStruc),
+        type: globalStruct,
         exposeSymbolAddress: true,
       ),
       Global(
         name: 'globalStruct_from_alias',
-        type: Type.typealias(
-          Typealias(
-            name: 'EmptyStruct_Alias',
-            type: Type.struct(globalStruc),
-          ),
+        type: Typealias(
+          name: 'EmptyStruct_Alias',
+          type: globalStruct,
         ),
         exposeSymbolAddress: true,
       )
diff --git a/pkgs/ffigen/test/header_parser_tests/native_func_typedef.h b/pkgs/ffigen/test/header_parser_tests/native_func_typedef.h
index 8b942d0..0c6f7a0 100644
--- a/pkgs/ffigen/test/header_parser_tests/native_func_typedef.h
+++ b/pkgs/ffigen/test/header_parser_tests/native_func_typedef.h
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-struct Struc
+struct Struct
 {
     void (*unnamed1)(void (*unnamed2)());
 };
@@ -17,6 +17,6 @@
 void funcWithNativeFunc(WithTypedefReturnType named);
 
 typedef void (*VoidFuncPointer)();
-struct Struc2{
+struct Struct2{
     const VoidFuncPointer constFuncPointer;
 };
diff --git a/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart b/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart
index 10dc390..70c205d 100644
--- a/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart
@@ -61,55 +61,55 @@
 }
 
 Library expectedLibrary() {
-  final struc2 = Struc(name: 'Struct2', members: [
+  final struct2 = Struct(name: 'Struct2', members: [
     Member(
       name: 'e',
-      type: Type.importedType(intType),
+      type: intType,
     ),
     Member(
       name: 'f',
-      type: Type.importedType(intType),
+      type: intType,
     ),
   ]);
-  final unnamedInternalStruc = Struc(name: 'UnnamedStruct1', members: [
+  final unnamedInternalStruct = Struct(name: 'UnnamedStruct1', members: [
     Member(
       name: 'a',
-      type: Type.importedType(intType),
+      type: intType,
     ),
     Member(
       name: 'b',
-      type: Type.importedType(intType),
+      type: intType,
     ),
   ]);
   return Library(
     name: 'Bindings',
     bindings: [
-      unnamedInternalStruc,
-      struc2,
-      Struc(name: 'Struct1', members: [
+      unnamedInternalStruct,
+      struct2,
+      Struct(name: 'Struct1', members: [
         Member(
           name: 'a',
-          type: Type.importedType(intType),
+          type: intType,
         ),
         Member(
           name: 'b',
-          type: Type.importedType(intType),
+          type: intType,
         ),
-        Member(name: 'struct2', type: Type.pointer(Type.struct(struc2))),
+        Member(name: 'struct2', type: PointerType(struct2)),
       ]),
-      Struc(name: 'Struct3', members: [
+      Struct(name: 'Struct3', members: [
         Member(
           name: 'a',
-          type: Type.importedType(intType),
+          type: intType,
         ),
         Member(
           name: 'b',
-          type: Type.struct(unnamedInternalStruc),
+          type: unnamedInternalStruct,
         ),
       ]),
-      Struc(name: 'EmptyStruct'),
-      Struc(name: 'Struct4'),
-      Struc(name: 'Struct5'),
+      Struct(name: 'EmptyStruct'),
+      Struct(name: 'Struct4'),
+      Struct(name: 'Struct5'),
     ],
   );
 }
diff --git a/pkgs/ffigen/test/rename_tests/rename_test.dart b/pkgs/ffigen/test/rename_tests/rename_test.dart
index 896b3db..0b976a8 100644
--- a/pkgs/ffigen/test/rename_tests/rename_test.dart
+++ b/pkgs/ffigen/test/rename_tests/rename_test.dart
@@ -161,109 +161,109 @@
 }
 
 Library expectedLibrary() {
-  final struc1 = Struc(name: '${structPrefix}Struct1');
-  final struc2 = Struc(name: 'Struct2');
-  final struc3 = Struc(name: 'Struct3');
+  final struct1 = Struct(name: '${structPrefix}Struct1');
+  final struct2 = Struct(name: 'Struct2');
+  final struct3 = Struct(name: 'Struct3');
   return Library(
     name: 'Bindings',
     bindings: [
       Func(
         name: '${functionPrefix}func1',
         originalName: 'func1',
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
         parameters: [
           Parameter(
             name: 's',
-            type: Type.pointer(Type.struct(struc1)),
+            type: PointerType(struct1),
           ),
         ],
       ),
       Func(
         name: 'func2',
         originalName: 'test_func2',
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
         parameters: [
           Parameter(
             name: 's',
-            type: Type.pointer(Type.struct(struc2)),
+            type: PointerType(struct2),
           ),
         ],
       ),
       Func(
         name: 'func3',
         originalName: 'fullMatch_func3',
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
         parameters: [
           Parameter(
             name: 's',
-            type: Type.pointer(Type.struct(struc3)),
+            type: PointerType(struct3),
           ),
         ],
       ),
       Func(
         name: '${functionPrefix}memberRename_func4',
         originalName: 'memberRename_func4',
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
         parameters: [
           Parameter(
             name: 'underscore',
-            type: Type.importedType(intType),
+            type: intType,
           ),
           Parameter(
             name: 'fullMatchSuccess',
-            type: Type.importedType(floatType),
+            type: floatType,
           ),
           Parameter(
             name: 'unnamed',
-            type: Type.importedType(intType),
+            type: intType,
           ),
         ],
       ),
       Func(
         name: '${functionPrefix}typedefRenameFunc',
         originalName: 'typedefRenameFunc',
-        returnType: Type.nativeType(
+        returnType: NativeType(
           SupportedNativeType.Void,
         ),
         parameters: [
           Parameter(
             name: 's',
-            type: Type.typealias(Typealias(
+            type: Typealias(
                 name: 'Struct5_Alias_Renamed',
-                type: Type.struct(Struc(name: '${structPrefix}Struct5')))),
+                type: Struct(name: '${structPrefix}Struct5')),
           ),
         ],
       ),
-      struc1,
-      struc2,
-      struc3,
-      Struc(
+      struct1,
+      struct2,
+      struct3,
+      Struct(
         name: '${structPrefix}MemberRenameStruct4',
         members: [
           Member(
             name: 'underscore',
-            type: Type.importedType(intType),
+            type: intType,
           ),
           Member(
             name: 'fullMatchSuccess',
-            type: Type.importedType(floatType),
+            type: floatType,
           ),
         ],
       ),
-      Struc(
+      Struct(
         name: '${structPrefix}AnyMatchStruct5',
         members: [
           Member(
             name: 'underscore',
-            type: Type.importedType(intType),
+            type: intType,
           ),
         ],
       ),