Type alias code generation
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 6eac0d9..e3026ba 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -3,7 +3,7 @@
 use crate::syntax::report::Errors;
 use crate::syntax::symbol::Symbol;
 use crate::syntax::{
-    self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types,
+    self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote, quote_spanned, ToTokens};
@@ -46,7 +46,7 @@
             Api::Enum(enm) => expanded.extend(expand_enum(enm)),
             Api::CxxType(ety) => {
                 if !types.enums.contains_key(&ety.ident) {
-                    expanded.extend(expand_cxx_type(ety));
+                    expanded.extend(expand_cxx_type(namespace, ety));
                 }
             }
             Api::CxxFunction(efn) => {
@@ -55,7 +55,10 @@
             Api::RustFunction(efn) => {
                 hidden.extend(expand_rust_function_shim(namespace, efn, types))
             }
-            Api::TypeAlias(_alias) => unimplemented!(),
+            Api::TypeAlias(alias) => {
+                expanded.extend(expand_type_alias(alias));
+                hidden.extend(expand_type_alias_verify(namespace, alias));
+            }
         }
     }
 
@@ -162,15 +165,21 @@
     }
 }
 
-fn expand_cxx_type(ety: &ExternType) -> TokenStream {
+fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream {
     let ident = &ety.ident;
     let doc = &ety.doc;
+    let type_id = type_id(namespace, ident);
+
     quote! {
         #doc
         #[repr(C)]
         pub struct #ident {
             _private: ::cxx::private::Opaque,
         }
+
+        unsafe impl ::cxx::ExternType for #ident {
+            type Id = #type_id;
+        }
     }
 }
 
@@ -555,6 +564,35 @@
     }
 }
 
+fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
+    let ident = &alias.ident;
+    let ty = &alias.ty;
+    quote! {
+        pub type #ident = #ty;
+    }
+}
+
+fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream {
+    let ident = &alias.ident;
+    let type_id = type_id(namespace, ident);
+    quote! {
+        const _: fn() = ::cxx::private::verify_extern_type::<#ident, #type_id>;
+    }
+}
+
+fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream {
+    let mut path = String::new();
+    for name in namespace {
+        path += &name.to_string();
+        path += "::";
+    }
+    path += &ident.to_string();
+
+    quote! {
+        ::cxx::type_id!(#path)
+    }
+}
+
 fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
     let link_prefix = format!("cxxbridge03$box${}{}$", namespace, ident);
     let link_uninit = format!("{}uninit", link_prefix);
diff --git a/src/extern_type.rs b/src/extern_type.rs
new file mode 100644
index 0000000..f7c376b
--- /dev/null
+++ b/src/extern_type.rs
@@ -0,0 +1,6 @@
+pub unsafe trait ExternType {
+    type Id;
+}
+
+#[doc(hidden)]
+pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
diff --git a/src/lib.rs b/src/lib.rs
index 39b1ecc..d15d441 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -369,6 +369,7 @@
 mod cxx_string;
 mod cxx_vector;
 mod exception;
+mod extern_type;
 mod function;
 mod opaque;
 mod result;
@@ -386,6 +387,7 @@
 pub use crate::cxx_string::CxxString;
 pub use crate::cxx_vector::CxxVector;
 pub use crate::exception::Exception;
+pub use crate::extern_type::ExternType;
 pub use crate::unique_ptr::UniquePtr;
 pub use cxxbridge_macro::{bridge, type_id};
 
@@ -393,6 +395,7 @@
 #[doc(hidden)]
 pub mod private {
     pub use crate::cxx_vector::VectorElement;
+    pub use crate::extern_type::verify_extern_type;
     pub use crate::function::FatFunction;
     pub use crate::opaque::Opaque;
     pub use crate::result::{r#try, Result};