diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91598a36..f530909f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,10 @@
 # Changelog
 
-## 4.1.2
+## 4.2.0
 
 - Generated code ignores more lints.
+- Add a workaround so "polymorphism" features can be used with dart2js.
+  See example/lib/polymorphism.dart.
 
 ## 4.1.1
 
diff --git a/built_value_generator/lib/src/value_source_class.dart b/built_value_generator/lib/src/value_source_class.dart
index e548b28c..f4f57cf5 100644
--- a/built_value_generator/lib/src/value_source_class.dart
+++ b/built_value_generator/lib/src/value_source_class.dart
@@ -29,6 +29,10 @@ abstract class ValueSourceClass
   @memoized
   ClassElement get builderElement => element.library.getType(name + 'Builder');
 
+  @memoized
+  bool get implementsBuilt => element.allSupertypes
+      .any((interfaceType) => interfaceType.name == 'Built');
+
   @memoized
   BuiltValue get settings {
     final annotations = element.metadata
@@ -146,9 +150,12 @@ abstract class ValueSourceClass
 
   static bool needsBuiltValue(ClassElement classElement) {
     // TODO(davidmorgan): more exact type check.
-    return classElement.allSupertypes
-            .any((interfaceType) => interfaceType.name == 'Built') &&
-        !classElement.displayName.startsWith('_\$');
+    return !classElement.displayName.startsWith('_\$') &&
+        (classElement.allSupertypes
+                .any((interfaceType) => interfaceType.name == 'Built') ||
+            classElement.metadata
+                .map((annotation) => annotation.computeConstantValue())
+                .any((value) => value?.type?.displayName == 'BuiltValue'));
   }
 
   Iterable<String> _computeErrors() {
@@ -172,10 +179,13 @@ abstract class ValueSourceClass
       result.add('Make class abstract.');
     }
 
-    final expectedBuildParameters = '$name$_generics, ${name}Builder$_generics';
-    if (builtParameters != expectedBuildParameters) {
-      result.add('Make class implement Built<$expectedBuildParameters>. '
-          'Currently: Built<$builtParameters>');
+    if (settings.instantiable) {
+      final expectedBuildParameters =
+          '$name$_generics, ${name}Builder$_generics';
+      if (builtParameters != expectedBuildParameters) {
+        result.add('Make class implement Built<$expectedBuildParameters>. '
+            'Currently: Built<$builtParameters>');
+      }
     }
 
     if (settings.instantiable) {
@@ -552,8 +562,19 @@ abstract class ValueSourceClass
   /// Generates an abstract builder with just abstract setters and getters.
   String _generateAbstractBuilder() {
     final result = new StringBuffer();
-    result.writeln('abstract class ${name}Builder$_boundedGenerics '
-        'implements ${builderImplements.join(", ")} {');
+
+    if (implementsBuilt) {
+      result.writeln('abstract class ${name}Builder$_boundedGenerics '
+          'implements ${builderImplements.join(", ")} {');
+    } else {
+      // The "Built" interface has been omitted to work around dart2js issue
+      // https://github.com/dart-lang/sdk/issues/14729. So, we can't implement
+      // "Builder". Add the methods explicitly.
+      result.writeln('abstract class ${name}Builder$_boundedGenerics {');
+
+      result.writeln('void replace($name$_generics other);');
+      result.writeln('void update(void updates(${name}Builder$_generics b));');
+    }
 
     for (final field in fields) {
       final typeInBuilder = field.typeInBuilder;
diff --git a/built_value_generator/lib/src/value_source_class.g.dart b/built_value_generator/lib/src/value_source_class.g.dart
index 586619d4..818252e4 100644
--- a/built_value_generator/lib/src/value_source_class.g.dart
+++ b/built_value_generator/lib/src/value_source_class.g.dart
@@ -19,6 +19,7 @@ class _$ValueSourceClass extends ValueSourceClass {
   final ClassElement element;
   String __name;
   ClassElement __builderElement;
+  bool __implementsBuilt;
   BuiltValue __settings;
   BuiltList<String> __genericParameters;
   BuiltList<String> __genericBounds;
@@ -50,6 +51,9 @@ class _$ValueSourceClass extends ValueSourceClass {
   @override
   ClassElement get builderElement => __builderElement ??= super.builderElement;
 
+  @override
+  bool get implementsBuilt => __implementsBuilt ??= super.implementsBuilt;
+
   @override
   BuiltValue get settings => __settings ??= super.settings;
 
diff --git a/example/lib/polymorphism.dart b/example/lib/polymorphism.dart
index cf00f805..40379218 100644
--- a/example/lib/polymorphism.dart
+++ b/example/lib/polymorphism.dart
@@ -18,6 +18,9 @@ part 'polymorphism.g.dart';
 /// Very little code is generated for a non-instantiable Built Value; just the
 /// interface for the builder. You can write this yourself if you prefer, then
 /// nothing will be generated.
+///
+/// Note that this type of inheritance is currently not supported by dart2js.
+/// See [Dart2jsCompatibleAnimal].
 @BuiltValue(instantiable: false)
 abstract class Animal extends Object
     with Walker
@@ -26,6 +29,20 @@ abstract class Animal extends Object
   int get legs;
 }
 
+/// The dart2js issue https://github.com/dart-lang/sdk/issues/14729
+/// prevents this working when compiling to js. As a workaround, make `Animal`
+/// not implement `Built`, and add the `rebuild` and `toBuilder` methods to
+/// it explicitly if you need them.
+@BuiltValue(instantiable: false)
+abstract class Dart2jsCompatibleAnimal extends Object with Walker {
+  @override
+  int get legs;
+
+  Dart2jsCompatibleAnimal rebuild(
+      void updates(Dart2jsCompatibleAnimalBuilder b));
+  Dart2jsCompatibleAnimalBuilder toBuilder();
+}
+
 /// `Cat` implements the non-instantiable Built Value `Animal`. The generated
 /// builder `CatBuilder` automatically extends 'AnimalBuilder`. This allows you
 /// to use `Cat` as an `Animal`.
diff --git a/example/lib/polymorphism.g.dart b/example/lib/polymorphism.g.dart
index 633e2eb6..7cf3f4c1 100644
--- a/example/lib/polymorphism.g.dart
+++ b/example/lib/polymorphism.g.dart
@@ -112,6 +112,13 @@ abstract class AnimalBuilder implements Builder<Animal, AnimalBuilder> {
   set legs(int legs);
 }
 
+abstract class Dart2jsCompatibleAnimalBuilder {
+  void replace(Dart2jsCompatibleAnimal other);
+  void update(void updates(Dart2jsCompatibleAnimalBuilder b));
+  int get legs;
+  set legs(int legs);
+}
+
 class _$Cat extends Cat {
   @override
   final bool tail;