diff --git a/furst_optics/__init__.py b/furst_optics/__init__.py
index def3de1..adb9ab5 100644
--- a/furst_optics/__init__.py
+++ b/furst_optics/__init__.py
@@ -2,6 +2,7 @@
 An idealized raytrace model of the FURST optical system.
 """
 
+from . import typevars
 from . import abc
 from . import sources
 from . import apertures
@@ -10,6 +11,7 @@
 from . import detectors
 
 __all__ = [
+    "typevars",
     "abc",
     "sources",
     "apertures",
diff --git a/furst_optics/gratings/__init__.py b/furst_optics/gratings/__init__.py
index 572984c..bf42f89 100644
--- a/furst_optics/gratings/__init__.py
+++ b/furst_optics/gratings/__init__.py
@@ -2,7 +2,9 @@
 Models and measurements of the diffraction grating.
 """
 
-from ._gratings import Grating
+from ._gratings import (
+    Grating,
+)
 
 __all__ = [
     "Grating",
diff --git a/furst_optics/gratings/_gratings.py b/furst_optics/gratings/_gratings.py
index 0d123a7..da84f02 100644
--- a/furst_optics/gratings/_gratings.py
+++ b/furst_optics/gratings/_gratings.py
@@ -1,3 +1,4 @@
+from typing import Generic
 import dataclasses
 import astropy.units as u
 import named_arrays as na
@@ -16,6 +17,11 @@ class Grating(
     optika.mixins.Pitchable,
     optika.mixins.Translatable,
     furst_optics.abc.AbstractRowlandComponent,
+    Generic[
+        furst_optics.typevars.SagT,
+        furst_optics.typevars.MaterialT,
+        furst_optics.typevars.RulingT,
+    ],
 ):
     """
     A model of the FURST diffraction grating.
@@ -48,7 +54,9 @@ class Grating(
 
         # Define the grating model
         grating = furst_optics.gratings.Grating(
-            radius=-2 *  rowland_radius,
+            sag=optika.sags.SphericalSag(
+                radius=-2 * rowland_radius,
+            ),
             width_clear=na.Cartesian2dVectorArray(
                 x=1000 * u.mm,
                 y=20 * u.mm,
@@ -83,6 +91,11 @@ class Grating(
     The human-readable name of this optic.
     """
 
+    sag: furst_optics.typevars.SagT = None
+    """
+    The sag profile of the grating surface.
+    """
+
     radius: u.Quantity | na.AbstractScalar = 0 * u.mm
     """
     The radius of curvature of the optical surface.
@@ -98,13 +111,13 @@ class Grating(
     The height and width of the grating substrate.
     """
 
-    material: None | optika.materials.AbstractMaterial = None
+    material: furst_optics.typevars.MaterialT = None
     """
     The coating material used to make the optic reflective
     in the target spectral range.
     """
 
-    rulings: None | optika.rulings.AbstractRulings = None
+    rulings: furst_optics.typevars.RulingT = None
     """
     A model of the grating ruling spacing and profile.
     """
@@ -150,9 +163,7 @@ class Grating(
     def surface(self) -> optika.surfaces.Surface:
         return optika.surfaces.Surface(
             name=self.name,
-            sag=optika.sags.SphericalSag(
-                radius=self.radius,
-            ),
+            sag=self.sag,
             material=self.material,
             aperture=optika.apertures.RectangularAperture(
                 half_width=self.width_clear / 2,
diff --git a/furst_optics/gratings/_gratings_test.py b/furst_optics/gratings/_gratings_test.py
index 4da4cf9..e956ca5 100644
--- a/furst_optics/gratings/_gratings_test.py
+++ b/furst_optics/gratings/_gratings_test.py
@@ -9,7 +9,9 @@
     argnames="a",
     argvalues=[
         furst_optics.gratings.Grating(
-            radius=1000 * u.mm,
+            sag=optika.sags.SphericalSag(
+                radius=1000 * u.mm,
+            ),
             width_clear=10 * u.mm,
             width_mech=15 * u.mm,
             material=optika.materials.Mirror(),
diff --git a/furst_optics/typevars.py b/furst_optics/typevars.py
new file mode 100644
index 0000000..85ffd28
--- /dev/null
+++ b/furst_optics/typevars.py
@@ -0,0 +1,29 @@
+"""
+Type variables used by the generic types
+in this project.
+"""
+
+from typing import TypeVar
+import optika
+
+__all__ = [
+    "SagT",
+    "MaterialT",
+    "RulingT",
+]
+
+
+#: Sag type variable.
+#: Should be :obj:`None` or a subclass of
+#: :class:`optika.sags.AbstractSag`
+SagT = TypeVar("SagT", bound=None | optika.sags.AbstractSag)
+
+#: Material type variable.
+#: Should be :obj:`None` or a subclass of
+#: :class:`optika.materials.AbstractMaterial`
+MaterialT = TypeVar("MaterialT", bound=None | optika.materials.AbstractMaterial)
+
+#: Ruling type variable.
+#: Should be :obj:`None` or a subclass of
+#: :class:`optika.rulings.AbstractRulings`
+RulingT = TypeVar("RulingT", bound=None | optika.rulings.AbstractRulings)