diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php
index a85fac4..eed1da8 100644
--- a/core/lib/Drupal/Core/Config/Schema/Mapping.php
+++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php
@@ -88,7 +88,7 @@ public function set($property_name, $value, $notify = TRUE) {
     if ($notify && isset($this->parent)) {
       $this->parent->onChange($this->name);
     }
-    return $property;
+    return $this;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php
index 0fb2330..bbcb0bb 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php
@@ -105,7 +105,8 @@ public function set($property_name, $value, $notify = TRUE) {
       throw new \InvalidArgumentException(String::format('Unable to set unknown property @name.', array('@name' => $property_name)));
     }
     // This will throw an exception for unknown fields.
-    return $this->entity->set($property_name, $value, $notify);
+    $this->entity->set($property_name, $value, $notify);
+    return $this;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Field/FieldItemBase.php b/core/lib/Drupal/Core/Field/FieldItemBase.php
index db8601c..b9c90d9 100644
--- a/core/lib/Drupal/Core/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Field/FieldItemBase.php
@@ -165,6 +165,7 @@ public function set($property_name, $value, $notify = TRUE) {
     if ($notify) {
       $this->onChange($property_name);
     }
+    return $this;
   }
 
   /**
@@ -190,23 +191,26 @@ public function __isset($name) {
    * {@inheritdoc}
    */
   public function __unset($name) {
-    $this->set($name, NULL);
-    unset($this->values[$name]);
+    // Explicitly unset the property in $this->values if a non-defined
+    // property is unset, such that its key is removed from $this->values.
+    if (!$this->definition->getPropertyDefinition($name)) {
+      unset($this->values[$name]);
+    }
+    else {
+      $this->set($name, NULL);
+    }
   }
 
   /**
-   * Overrides \Drupal\Core\TypedData\Map::onChange().
+   * {@inheritdoc}
    */
   public function onChange($property_name) {
-    // Notify the parent of changes.
-    if (isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     // Remove the plain value, such that any further __get() calls go via the
     // updated property object.
     if (isset($this->properties[$property_name])) {
       unset($this->values[$property_name]);
     }
+    parent::onChange($property_name);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index e7b91fc..b60f84d 100644
--- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
@@ -53,8 +53,7 @@ public function get($property_name);
    *   TRUE. If the update stems from a parent object, set it to FALSE to avoid
    *   being notified again.
    *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
-   *   The property object.
+   * @return $this
    *
    * @throws \InvalidArgumentException
    *   If the specified property does not exist.
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
index abb1c9c..e17e072 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php
@@ -148,6 +148,7 @@ public function set($property_name, $value, $notify = TRUE) {
         $this->onChange($property_name, $value);
       }
     }
+    return $this;
   }
 
   /**
diff --git a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
index f63ffd6..d24f1c2 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
@@ -132,12 +132,11 @@ public function isEmpty() {
    * {@inheritdoc}
    */
   public function onChange($property_name) {
-    parent::onChange($property_name);
-
     // Enforce that the computed date is recalculated.
     if ($property_name == 'value') {
       $this->date = NULL;
     }
+    parent::onChange($property_name);
   }
 
 }
diff --git a/core/modules/system/src/Tests/Entity/EntityFieldTest.php b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
index 41dbb44..e975e9f 100644
--- a/core/modules/system/src/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
@@ -130,16 +130,16 @@ protected function doTestReadWrite($entity_type) {
     $this->assertEqual($this->entity_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type)));
 
     // Change the assigned user by entity.
-    $new_user = $this->createUser();
-    $entity->user_id->entity = $new_user;
-    $this->assertEqual($new_user->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
-    $this->assertEqual($new_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type)));
+    $new_user1 = $this->createUser();
+    $entity->user_id->entity = $new_user1;
+    $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
+    $this->assertEqual($new_user1->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type)));
 
     // Change the assigned user by id.
-    $new_user = $this->createUser();
-    $entity->user_id->target_id = $new_user->id();
-    $this->assertEqual($new_user->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
-    $this->assertEqual($new_user->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type)));
+    $new_user2 = $this->createUser();
+    $entity->user_id->target_id = $new_user2->id();
+    $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
+    $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type)));
 
     // Try unsetting a field.
     $entity->name->value = NULL;
@@ -148,6 +148,34 @@ protected function doTestReadWrite($entity_type) {
     $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type)));
     $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type)));
 
+    // Test setting the values via the typed data API works as well.
+    // Change the assigned user by entity.
+    $entity->user_id->first()->get('entity')->setValue($new_user2);
+    $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
+    $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
+
+     // Change the assigned user by id.
+    $entity->user_id->first()->get('target_id')->setValue($new_user2->id());
+    $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
+    $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
+
+    // Try unsetting a field.
+    $entity->name->first()->get('value')->setValue(NULL);
+    $entity->user_id->first()->get('target_id')->setValue(NULL);
+    $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type)));
+    $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type)));
+    $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type)));
+
+    // Create a fresh entity so target_id does not get its property object
+    // instantiated, then verify setting a new value via typed data API works.
+    $entity2 = entity_create($entity_type, array(
+      'user_id' => array('target_id' => $new_user2->id()),
+    ));
+    // Access the property object, and set a value.
+    $entity2->user_id->first()->get('target_id')->setValue($new_user2->id());
+    $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
+    $this->assertEqual($new_user2->getUsername(), $entity2->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
+
     // Test using isset(), empty() and unset().
     $entity->name->value = 'test unset';
     unset($entity->name->value);
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php b/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php
index e08f951..3d81f31 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextItemBase.php
@@ -60,11 +60,6 @@ public function isEmpty() {
    * {@inheritdoc}
    */
   public function onChange($property_name) {
-    // Notify the parent of changes.
-    if (isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-
     // Unset processed properties that are affected by the change.
     foreach ($this->definition->getPropertyDefinitions() as $property => $definition) {
       if ($definition->getClass() == '\Drupal\text\TextProcessed') {
@@ -73,6 +68,7 @@ public function onChange($property_name) {
         }
       }
     }
+    parent::onChange($property_name);
   }
 
   /**
