Add toObservableList and toObservableMap (#81)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6969548..db46b07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## HEAD
+
+* Add `toObservableList` and `toObservableMap`, better typed versions of
+  `toObservable`.
+
 ## 0.22.1+5
 
 Fix generic type error that occurs when using ChangeNotifier with a subclass of
diff --git a/lib/src/to_observable.dart b/lib/src/to_observable.dart
index d331913..20f8d7e 100644
--- a/lib/src/to_observable.dart
+++ b/lib/src/to_observable.dart
@@ -16,6 +16,9 @@
 /// respectively. This is a convenience function to make it easier to convert
 /// literals into the corresponding observable collection type.
 ///
+/// For better static typing, use either [toObservableList] or
+/// [toObservableMap] instead of this function.
+///
 /// If [value] is not one of those collection types, or is already [Observable],
 /// it will be returned unmodified.
 ///
@@ -30,6 +33,35 @@
 toObservable(dynamic value, {bool deep = true}) =>
     deep ? _toObservableDeep(value) : _toObservableShallow(value);
 
+/// Converts the [Iterable] to an [ObservableList].
+///
+/// If [value] is already [Observable], it will be returned unmodified.
+///
+/// By default this performs a deep conversion, but you can set [deep] to false
+/// for a shallow conversion. This does not handle circular data structures.
+/// If a conversion is peformed, mutations are only observed to the result of
+/// this function. Changing the original collection will not affect it.
+ObservableList<T> toObservableList<T>(Iterable<T> value, {bool deep: true}) {
+  if (value is Observable) return value;
+  return deep ? _toObservableDeepIterable(value) : _toObservableShallow(value);
+}
+
+/// Converts the [Map] to an [ObservableMap].
+///
+/// If [value] is already [Observable], it will be returned unmodified.
+///
+/// The returned value will use the appropriate kind of backing map: either
+/// [HashMap], [LinkedHashMap], or [SplayTreeMap].
+///
+/// By default this performs a deep conversion, but you can set [deep] to false
+/// for a shallow conversion. This does not handle circular data structures.
+/// If a conversion is peformed, mutations are only observed to the result of
+/// this function. Changing the original collection will not affect it.
+ObservableMap<K, V> toObservableMap<K, V>(Map<K, V> value, {bool deep: true}) {
+  if (value is Observable) return value;
+  return deep ? _toObservableDeepMap(value) : _toObservableShallow(value);
+}
+
 dynamic _toObservableShallow(dynamic value) {
   if (value is Observable) return value;
 
@@ -49,25 +81,29 @@
 dynamic _toObservableDeep(dynamic value) {
   if (value is Observable) return value;
 
-  if (value is Map) {
-    return extractMapTypeArguments(value, <K, V>() {
-      var result = new ObservableMap<K, V>.createFromType(value);
-      value.forEach((k, v) {
-        result[_toObservableDeep(k)] = _toObservableDeep(v);
-      });
-      return result;
-    });
-  }
+  if (value is Map) return _toObservableDeepMap(value);
 
-  if (value is Iterable) {
-    return extractIterableTypeArgument(value, <T>() {
-      var result = new ObservableList<T>();
-      for (var element in value) {
-        result.add(_toObservableDeep(element));
-      }
-      return result;
-    });
-  }
+  if (value is Iterable) return _toObservableDeepIterable(value);
 
   return value;
 }
+
+ObservableMap _toObservableDeepMap(Map<dynamic, dynamic> value) {
+  return extractMapTypeArguments(value, <K, V>() {
+    var result = new ObservableMap<K, V>.createFromType(value);
+    value.forEach((k, v) {
+      result[_toObservableDeep(k)] = _toObservableDeep(v);
+    });
+    return result;
+  });
+}
+
+ObservableList _toObservableDeepIterable(Iterable<dynamic> value) {
+  return extractIterableTypeArgument(value, <T>() {
+    var result = new ObservableList<T>();
+    for (var element in value) {
+      result.add(_toObservableDeep(element));
+    }
+    return result;
+  });
+}