From 499cd37b06577595bb68f843590ec356608dc9fb Mon Sep 17 00:00:00 2001
From: Andre Staltz <andre@staltz.com>
Date: Tue, 8 Nov 2016 20:25:23 +0200
Subject: [PATCH] Migrate source code to TypeScript v2.0

---
 .gitignore                                    |  24 ++
 h.js                                          |  34 ---
 htmldomapi.js                                 |  54 ----
 is.js                                         |   4 -
 modules/class.js                              |  23 --
 modules/dataset.js                            |  23 --
 modules/props.js                              |  23 --
 modules/style.js                              |  69 -----
 package.json                                  |   6 +-
 snabbdom.bundle.js                            |  11 -
 snabbdom.js                                   | 260 -----------------
 src/h.ts                                      |  42 +++
 .../attachto.js => src/helpers/attachto.ts    |  28 +-
 src/htmldomapi.ts                             |  54 ++++
 src/interfaces.d.ts                           |  87 ++++++
 src/is.ts                                     |   6 +
 .../modules/attributes.ts                     |  25 +-
 src/modules/class.ts                          |  25 ++
 src/modules/dataset.ts                        |  25 ++
 .../modules/eventlisteners.ts                 |  32 ++-
 modules/hero.js => src/modules/hero.ts        |  87 +++---
 src/modules/props.ts                          |  26 ++
 src/modules/style.ts                          |  76 +++++
 src/snabbdom.bundle.ts                        |  16 ++
 src/snabbdom.ts                               | 272 ++++++++++++++++++
 src/thunk.ts                                  |  49 ++++
 src/vnode.ts                                  |  13 +
 thunk.js                                      |  46 ---
 tsconfig.json                                 |  35 +++
 type-definitions/snabbdom.d.ts                | 148 ----------
 vnode.js                                      |   5 -
 31 files changed, 849 insertions(+), 779 deletions(-)
 delete mode 100644 h.js
 delete mode 100644 htmldomapi.js
 delete mode 100644 is.js
 delete mode 100644 modules/class.js
 delete mode 100644 modules/dataset.js
 delete mode 100644 modules/props.js
 delete mode 100644 modules/style.js
 delete mode 100644 snabbdom.bundle.js
 delete mode 100644 snabbdom.js
 create mode 100644 src/h.ts
 rename helpers/attachto.js => src/helpers/attachto.ts (53%)
 mode change 100644 => 100755
 create mode 100644 src/htmldomapi.ts
 create mode 100644 src/interfaces.d.ts
 create mode 100644 src/is.ts
 rename modules/attributes.js => src/modules/attributes.ts (60%)
 mode change 100644 => 100755
 create mode 100755 src/modules/class.ts
 create mode 100755 src/modules/dataset.ts
 rename modules/eventlisteners.js => src/modules/eventlisteners.ts (74%)
 mode change 100644 => 100755
 rename modules/hero.js => src/modules/hero.ts (55%)
 mode change 100644 => 100755
 create mode 100755 src/modules/props.ts
 create mode 100755 src/modules/style.ts
 create mode 100644 src/snabbdom.bundle.ts
 create mode 100644 src/snabbdom.ts
 create mode 100644 src/thunk.ts
 create mode 100644 src/vnode.ts
 delete mode 100644 thunk.js
 create mode 100644 tsconfig.json
 delete mode 100644 type-definitions/snabbdom.d.ts
 delete mode 100644 vnode.js

diff --git a/.gitignore b/.gitignore
index 6c739de..79fbad0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,5 +29,29 @@ node_modules
 # Vim
 *.swp
 
+# Generated JavaScript
 test/browserified.js
 browserified.js
+h.d.ts
+h.js
+h.js.map
+htmldomapi.d.ts
+htmldomapi.js
+htmldomapi.js.map
+is.d.ts
+is.js
+is.js.map
+snabbdom.bundle.d.ts
+snabbdom.bundle.js
+snabbdom.bundle.js.map
+snabbdom.d.ts
+snabbdom.js
+snabbdom.js.map
+thunk.d.ts
+thunk.js
+thunk.js.map
+vnode.d.ts
+vnode.js
+vnode.js.map
+/modules
+/helpers
diff --git a/h.js b/h.js
deleted file mode 100644
index ac07850..0000000
--- a/h.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var VNode = require('./vnode');
-var is = require('./is');
-
-function addNS(data, children, sel) {
-  data.ns = 'http://www.w3.org/2000/svg';
-
-  if (sel !== 'foreignObject' && children !== undefined) {
-    for (var i = 0; i < children.length; ++i) {
-      addNS(children[i].data, children[i].children, children[i].sel);
-    }
-  }
-}
-
-module.exports = function h(sel, b, c) {
-  var data = {}, children, text, i;
-  if (c !== undefined) {
-    data = b;
-    if (is.array(c)) { children = c; }
-    else if (is.primitive(c)) { text = c; }
-  } else if (b !== undefined) {
-    if (is.array(b)) { children = b; }
-    else if (is.primitive(b)) { text = b; }
-    else { data = b; }
-  }
-  if (is.array(children)) {
-    for (i = 0; i < children.length; ++i) {
-      if (is.primitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i]);
-    }
-  }
-  if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') {
-    addNS(data, children, sel);
-  }
-  return VNode(sel, data, children, text, undefined);
-};
diff --git a/htmldomapi.js b/htmldomapi.js
deleted file mode 100644
index c73f29a..0000000
--- a/htmldomapi.js
+++ /dev/null
@@ -1,54 +0,0 @@
-function createElement(tagName){
-  return document.createElement(tagName);
-}
-
-function createElementNS(namespaceURI, qualifiedName){
-  return document.createElementNS(namespaceURI, qualifiedName);
-}
-
-function createTextNode(text){
-  return document.createTextNode(text);
-}
-
-
-function insertBefore(parentNode, newNode, referenceNode){
-  parentNode.insertBefore(newNode, referenceNode);
-}
-
-
-function removeChild(node, child){
-  node.removeChild(child);
-}
-
-function appendChild(node, child){
-  node.appendChild(child);
-}
-
-function parentNode(node){
-  return node.parentElement;
-}
-
-function nextSibling(node){
-  return node.nextSibling;
-}
-
-function tagName(node){
-  return node.tagName;
-}
-
-function setTextContent(node, text){
-  node.textContent = text;
-}
-
-module.exports = {
-  createElement: createElement,
-  createElementNS: createElementNS,
-  createTextNode: createTextNode,
-  appendChild: appendChild,
-  removeChild: removeChild,
-  insertBefore: insertBefore,
-  parentNode: parentNode,
-  nextSibling: nextSibling,
-  tagName: tagName,
-  setTextContent: setTextContent
-};
diff --git a/is.js b/is.js
deleted file mode 100644
index c228508..0000000
--- a/is.js
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = {
-  array: Array.isArray,
-  primitive: function(s) { return typeof s === 'string' || typeof s === 'number'; },
-};
diff --git a/modules/class.js b/modules/class.js
deleted file mode 100644
index 35e19de..0000000
--- a/modules/class.js
+++ /dev/null
@@ -1,23 +0,0 @@
-function updateClass(oldVnode, vnode) {
-  var cur, name, elm = vnode.elm,
-      oldClass = oldVnode.data.class,
-      klass = vnode.data.class;
-
-  if (!oldClass && !klass) return;
-  oldClass = oldClass || {};
-  klass = klass || {};
-
-  for (name in oldClass) {
-    if (!klass[name]) {
-      elm.classList.remove(name);
-    }
-  }
-  for (name in klass) {
-    cur = klass[name];
-    if (cur !== oldClass[name]) {
-      elm.classList[cur ? 'add' : 'remove'](name);
-    }
-  }
-}
-
-module.exports = {create: updateClass, update: updateClass};
diff --git a/modules/dataset.js b/modules/dataset.js
deleted file mode 100644
index 37e573b..0000000
--- a/modules/dataset.js
+++ /dev/null
@@ -1,23 +0,0 @@
-function updateDataset(oldVnode, vnode) {
-  var elm = vnode.elm,
-    oldDataset = oldVnode.data.dataset,
-    dataset = vnode.data.dataset,
-    key
-
-  if (!oldDataset && !dataset) return;
-  oldDataset = oldDataset || {};
-  dataset = dataset || {};
-
-  for (key in oldDataset) {
-    if (!dataset[key]) {
-      delete elm.dataset[key];
-    }
-  }
-  for (key in dataset) {
-    if (oldDataset[key] !== dataset[key]) {
-      elm.dataset[key] = dataset[key];
-    }
-  }
-}
-
-module.exports = {create: updateDataset, update: updateDataset}
diff --git a/modules/props.js b/modules/props.js
deleted file mode 100644
index 843c8a8..0000000
--- a/modules/props.js
+++ /dev/null
@@ -1,23 +0,0 @@
-function updateProps(oldVnode, vnode) {
-  var key, cur, old, elm = vnode.elm,
-      oldProps = oldVnode.data.props, props = vnode.data.props;
-
-  if (!oldProps && !props) return;
-  oldProps = oldProps || {};
-  props = props || {};
-
-  for (key in oldProps) {
-    if (!props[key]) {
-      delete elm[key];
-    }
-  }
-  for (key in props) {
-    cur = props[key];
-    old = oldProps[key];
-    if (old !== cur && (key !== 'value' || elm[key] !== cur)) {
-      elm[key] = cur;
-    }
-  }
-}
-
-module.exports = {create: updateProps, update: updateProps};
diff --git a/modules/style.js b/modules/style.js
deleted file mode 100644
index e03b633..0000000
--- a/modules/style.js
+++ /dev/null
@@ -1,69 +0,0 @@
-var raf = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout;
-var nextFrame = function(fn) { raf(function() { raf(fn); }); };
-
-function setNextFrame(obj, prop, val) {
-  nextFrame(function() { obj[prop] = val; });
-}
-
-function updateStyle(oldVnode, vnode) {
-  var cur, name, elm = vnode.elm,
-      oldStyle = oldVnode.data.style,
-      style = vnode.data.style;
-
-  if (!oldStyle && !style) return;
-  oldStyle = oldStyle || {};
-  style = style || {};
-  var oldHasDel = 'delayed' in oldStyle;
-
-  for (name in oldStyle) {
-    if (!style[name]) {
-      elm.style[name] = '';
-    }
-  }
-  for (name in style) {
-    cur = style[name];
-    if (name === 'delayed') {
-      for (name in style.delayed) {
-        cur = style.delayed[name];
-        if (!oldHasDel || cur !== oldStyle.delayed[name]) {
-          setNextFrame(elm.style, name, cur);
-        }
-      }
-    } else if (name !== 'remove' && cur !== oldStyle[name]) {
-      elm.style[name] = cur;
-    }
-  }
-}
-
-function applyDestroyStyle(vnode) {
-  var style, name, elm = vnode.elm, s = vnode.data.style;
-  if (!s || !(style = s.destroy)) return;
-  for (name in style) {
-    elm.style[name] = style[name];
-  }
-}
-
-function applyRemoveStyle(vnode, rm) {
-  var s = vnode.data.style;
-  if (!s || !s.remove) {
-    rm();
-    return;
-  }
-  var name, elm = vnode.elm, idx, i = 0, maxDur = 0,
-      compStyle, style = s.remove, amount = 0, applied = [];
-  for (name in style) {
-    applied.push(name);
-    elm.style[name] = style[name];
-  }
-  compStyle = getComputedStyle(elm);
-  var props = compStyle['transition-property'].split(', ');
-  for (; i < props.length; ++i) {
-    if(applied.indexOf(props[i]) !== -1) amount++;
-  }
-  elm.addEventListener('transitionend', function(ev) {
-    if (ev.target === elm) --amount;
-    if (amount === 0) rm();
-  });
-}
-
-module.exports = {create: updateStyle, update: updateStyle, destroy: applyDestroyStyle, remove: applyRemoveStyle};
diff --git a/package.json b/package.json
index 1aceea4..269e48f 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
   "version": "0.5.4",
   "description": "A virtual DOM library with focus on simplicity, modularity, powerful features and performance.",
   "main": "snabbdom.js",
-  "typings": "type-definitions/snabbdom.d.ts",
+  "typings": "snabbdom.d.ts",
   "directories": {
     "example": "examples",
     "test": "test"
@@ -20,10 +20,14 @@
     "gulp-uglify": "^1.5.3",
     "knuth-shuffle": "^1.0.1",
     "testem": "^1.0.2",
+    "typescript": "^2.0.6",
     "xyz": "0.5.x"
   },
   "scripts": {
+    "pretest": "npm run compile",
     "test": "testem",
+    "compile": "tsc",
+    "prepublish": "npm run compile",
     "release-major": "xyz --repo git@github.com:paldepind/snabbdom.git --increment major",
     "release-minor": "xyz --repo git@github.com:paldepind/snabbdom.git --increment minor",
     "release-patch": "xyz --repo git@github.com:paldepind/snabbdom.git --increment patch"
diff --git a/snabbdom.bundle.js b/snabbdom.bundle.js
deleted file mode 100644
index 2f26dec..0000000
--- a/snabbdom.bundle.js
+++ /dev/null
@@ -1,11 +0,0 @@
-var snabbdom = require('./snabbdom');
-var patch = snabbdom.init([ // Init patch function with choosen modules
-  require('./modules/attributes'), // makes it easy to toggle classes
-  require('./modules/class'), // makes it easy to toggle classes
-  require('./modules/props'), // for setting properties on DOM elements
-  require('./modules/style'), // handles styling on elements with support for animations
-  require('./modules/eventlisteners'), // attaches event listeners
-]);
-var h = require('./h'); // helper function for creating vnodes
-
-module.exports = { patch: patch, h: h }
diff --git a/snabbdom.js b/snabbdom.js
deleted file mode 100644
index 1fcc2ed..0000000
--- a/snabbdom.js
+++ /dev/null
@@ -1,260 +0,0 @@
-// jshint newcap: false
-/* global require, module, document, Node */
-'use strict';
-
-var VNode = require('./vnode');
-var is = require('./is');
-var domApi = require('./htmldomapi');
-
-function isUndef(s) { return s === undefined; }
-function isDef(s) { return s !== undefined; }
-
-var emptyNode = VNode('', {}, [], undefined, undefined);
-
-function sameVnode(vnode1, vnode2) {
-  return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
-}
-
-function createKeyToOldIdx(children, beginIdx, endIdx) {
-  var i, map = {}, key;
-  for (i = beginIdx; i <= endIdx; ++i) {
-    key = children[i].key;
-    if (isDef(key)) map[key] = i;
-  }
-  return map;
-}
-
-var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];
-
-function init(modules, api) {
-  var i, j, cbs = {};
-
-  if (isUndef(api)) api = domApi;
-
-  for (i = 0; i < hooks.length; ++i) {
-    cbs[hooks[i]] = [];
-    for (j = 0; j < modules.length; ++j) {
-      if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]);
-    }
-  }
-
-  function emptyNodeAt(elm) {
-    var id = elm.id ? '#' + elm.id : '';
-    var c = elm.className ? '.' + elm.className.split(' ').join('.') : '';
-    return VNode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
-  }
-
-  function createRmCb(childElm, listeners) {
-    return function() {
-      if (--listeners === 0) {
-        var parent = api.parentNode(childElm);
-        api.removeChild(parent, childElm);
-      }
-    };
-  }
-
-  function createElm(vnode, insertedVnodeQueue) {
-    var i, data = vnode.data;
-    if (isDef(data)) {
-      if (isDef(i = data.hook) && isDef(i = i.init)) {
-        i(vnode);
-        data = vnode.data;
-      }
-    }
-    var elm, children = vnode.children, sel = vnode.sel;
-    if (isDef(sel)) {
-      // Parse selector
-      var hashIdx = sel.indexOf('#');
-      var dotIdx = sel.indexOf('.', hashIdx);
-      var hash = hashIdx > 0 ? hashIdx : sel.length;
-      var dot = dotIdx > 0 ? dotIdx : sel.length;
-      var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel;
-      elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag)
-                                                          : api.createElement(tag);
-      if (hash < dot) elm.id = sel.slice(hash + 1, dot);
-      if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/\./g, ' ');
-      if (is.array(children)) {
-        for (i = 0; i < children.length; ++i) {
-          api.appendChild(elm, createElm(children[i], insertedVnodeQueue));
-        }
-      } else if (is.primitive(vnode.text)) {
-        api.appendChild(elm, api.createTextNode(vnode.text));
-      }
-      for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode);
-      i = vnode.data.hook; // Reuse variable
-      if (isDef(i)) {
-        if (i.create) i.create(emptyNode, vnode);
-        if (i.insert) insertedVnodeQueue.push(vnode);
-      }
-    } else {
-      elm = vnode.elm = api.createTextNode(vnode.text);
-    }
-    return vnode.elm;
-  }
-
-  function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
-    for (; startIdx <= endIdx; ++startIdx) {
-      api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
-    }
-  }
-
-  function invokeDestroyHook(vnode) {
-    var i, j, data = vnode.data;
-    if (isDef(data)) {
-      if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode);
-      for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode);
-      if (isDef(i = vnode.children)) {
-        for (j = 0; j < vnode.children.length; ++j) {
-          invokeDestroyHook(vnode.children[j]);
-        }
-      }
-    }
-  }
-
-  function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
-    for (; startIdx <= endIdx; ++startIdx) {
-      var i, listeners, rm, ch = vnodes[startIdx];
-      if (isDef(ch)) {
-        if (isDef(ch.sel)) {
-          invokeDestroyHook(ch);
-          listeners = cbs.remove.length + 1;
-          rm = createRmCb(ch.elm, listeners);
-          for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm);
-          if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) {
-            i(ch, rm);
-          } else {
-            rm();
-          }
-        } else { // Text node
-          api.removeChild(parentElm, ch.elm);
-        }
-      }
-    }
-  }
-
-  function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
-    var oldStartIdx = 0, newStartIdx = 0;
-    var oldEndIdx = oldCh.length - 1;
-    var oldStartVnode = oldCh[0];
-    var oldEndVnode = oldCh[oldEndIdx];
-    var newEndIdx = newCh.length - 1;
-    var newStartVnode = newCh[0];
-    var newEndVnode = newCh[newEndIdx];
-    var oldKeyToIdx, idxInOld, elmToMove, before;
-
-    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
-      if (isUndef(oldStartVnode)) {
-        oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
-      } else if (isUndef(oldEndVnode)) {
-        oldEndVnode = oldCh[--oldEndIdx];
-      } else if (sameVnode(oldStartVnode, newStartVnode)) {
-        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
-        oldStartVnode = oldCh[++oldStartIdx];
-        newStartVnode = newCh[++newStartIdx];
-      } else if (sameVnode(oldEndVnode, newEndVnode)) {
-        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
-        oldEndVnode = oldCh[--oldEndIdx];
-        newEndVnode = newCh[--newEndIdx];
-      } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
-        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
-        api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm));
-        oldStartVnode = oldCh[++oldStartIdx];
-        newEndVnode = newCh[--newEndIdx];
-      } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
-        patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
-        api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
-        oldEndVnode = oldCh[--oldEndIdx];
-        newStartVnode = newCh[++newStartIdx];
-      } else {
-        if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
-        idxInOld = oldKeyToIdx[newStartVnode.key];
-        if (isUndef(idxInOld)) { // New element
-          api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
-          newStartVnode = newCh[++newStartIdx];
-        } else {
-          elmToMove = oldCh[idxInOld];
-          patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
-          oldCh[idxInOld] = undefined;
-          api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);
-          newStartVnode = newCh[++newStartIdx];
-        }
-      }
-    }
-    if (oldStartIdx > oldEndIdx) {
-      before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm;
-      addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
-    } else if (newStartIdx > newEndIdx) {
-      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
-    }
-  }
-
-  function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
-    var i, hook;
-    if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
-      i(oldVnode, vnode);
-    }
-    var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children;
-    if (oldVnode === vnode) return;
-    if (!sameVnode(oldVnode, vnode)) {
-      var parentElm = api.parentNode(oldVnode.elm);
-      elm = createElm(vnode, insertedVnodeQueue);
-      api.insertBefore(parentElm, elm, oldVnode.elm);
-      removeVnodes(parentElm, [oldVnode], 0, 0);
-      return;
-    }
-    if (isDef(vnode.data)) {
-      for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
-      i = vnode.data.hook;
-      if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode);
-    }
-    if (isUndef(vnode.text)) {
-      if (isDef(oldCh) && isDef(ch)) {
-        if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue);
-      } else if (isDef(ch)) {
-        if (isDef(oldVnode.text)) api.setTextContent(elm, '');
-        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
-      } else if (isDef(oldCh)) {
-        removeVnodes(elm, oldCh, 0, oldCh.length - 1);
-      } else if (isDef(oldVnode.text)) {
-        api.setTextContent(elm, '');
-      }
-    } else if (oldVnode.text !== vnode.text) {
-      api.setTextContent(elm, vnode.text);
-    }
-    if (isDef(hook) && isDef(i = hook.postpatch)) {
-      i(oldVnode, vnode);
-    }
-  }
-
-  return function(oldVnode, vnode) {
-    var i, elm, parent;
-    var insertedVnodeQueue = [];
-    for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();
-
-    if (isUndef(oldVnode.sel)) {
-      oldVnode = emptyNodeAt(oldVnode);
-    }
-
-    if (sameVnode(oldVnode, vnode)) {
-      patchVnode(oldVnode, vnode, insertedVnodeQueue);
-    } else {
-      elm = oldVnode.elm;
-      parent = api.parentNode(elm);
-
-      createElm(vnode, insertedVnodeQueue);
-
-      if (parent !== null) {
-        api.insertBefore(parent, vnode.elm, api.nextSibling(elm));
-        removeVnodes(parent, [oldVnode], 0, 0);
-      }
-    }
-
-    for (i = 0; i < insertedVnodeQueue.length; ++i) {
-      insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
-    }
-    for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
-    return vnode;
-  };
-}
-
-module.exports = {init: init};
diff --git a/src/h.ts b/src/h.ts
new file mode 100644
index 0000000..7d387fb
--- /dev/null
+++ b/src/h.ts
@@ -0,0 +1,42 @@
+import {VNode} from './interfaces';
+import vnode = require('./vnode');
+import is = require('./is');
+
+function addNS(data: any, children: Array<VNode> | undefined, sel: string | undefined): void {
+  data.ns = 'http://www.w3.org/2000/svg';
+  if (sel !== 'foreignObject' && children !== undefined) {
+    for (let i = 0; i < children.length; ++i) {
+      addNS(children[i].data, (children[i] as VNode).children as Array<VNode>, children[i].sel);
+    }
+  }
+}
+
+function h(sel: string): VNode;
+function h(sel: string, data: any): VNode;
+function h(sel: string, text: string): VNode;
+function h(sel: string, children: Array<VNode>): VNode;
+function h(sel: string, data: any, text: string): VNode;
+function h(sel: string, data: any, children: Array<VNode>): VNode;
+function h(sel: any, b?: any, c?: any): VNode {
+  var data = {}, children: any, text: any, i: number;
+  if (c !== undefined) {
+    data = b;
+    if (is.array(c)) { children = c; }
+    else if (is.primitive(c)) { text = c; }
+  } else if (b !== undefined) {
+    if (is.array(b)) { children = b; }
+    else if (is.primitive(b)) { text = b; }
+    else { data = b; }
+  }
+  if (is.array(children)) {
+    for (i = 0; i < children.length; ++i) {
+      if (is.primitive(children[i])) children[i] = (vnode as any)(undefined, undefined, undefined, children[i]);
+    }
+  }
+  if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') {
+    addNS(data, children, sel);
+  }
+  return vnode(sel, data, children, text, undefined);
+};
+
+export = h;
\ No newline at end of file
diff --git a/helpers/attachto.js b/src/helpers/attachto.ts
old mode 100644
new mode 100755
similarity index 53%
rename from helpers/attachto.js
rename to src/helpers/attachto.ts
index 24c8d48..29e6feb
--- a/helpers/attachto.js
+++ b/src/helpers/attachto.ts
@@ -1,26 +1,28 @@
-function pre(vnode, newVnode) {
-  var attachData = vnode.data.attachData;
+import {VNode, VNodeData} from '../interfaces';
+
+function pre(vnode: VNode, newVnode: VNode): void {
+  var attachData = (vnode.data as VNodeData).attachData;
   // Copy created placeholder and real element from old vnode
-  newVnode.data.attachData.placeholder = attachData.placeholder;
-  newVnode.data.attachData.real = attachData.real;
+  (newVnode.data as VNodeData).attachData.placeholder = attachData.placeholder;
+  (newVnode.data as VNodeData).attachData.real = attachData.real;
   // Mount real element in vnode so the patch process operates on it
-  vnode.elm = vnode.data.attachData.real;
+  vnode.elm = (vnode.data as VNodeData).attachData.real;
 }
 
-function post(_, vnode) {
+function post(_: any, vnode: VNode): void {
   // Mount dummy placeholder in vnode so potential reorders use it
-  vnode.elm = vnode.data.attachData.placeholder;
+  vnode.elm = (vnode.data as VNodeData).attachData.placeholder;
 }
 
-function destroy(vnode) {
+function destroy(vnode: VNode): void {
   // Remove placeholder
-  vnode.elm.parentElement.removeChild(vnode.elm);
+  vnode.elm && vnode.elm.parentElement.removeChild(vnode.elm);
   // Remove real element from where it was inserted
-  vnode.elm = vnode.data.attachData.real;
+  vnode.elm = (vnode.data as VNodeData).attachData.real;
 }
 
-function create(_, vnode) {
-  var real = vnode.elm, attachData = vnode.data.attachData;
+function create(_: any, vnode: VNode): void {
+  var real = vnode.elm, attachData = (vnode.data as VNodeData).attachData;
   var placeholder = document.createElement('span');
   // Replace actual element with dummy placeholder
   // Snabbdom will then insert placeholder instead
@@ -30,7 +32,7 @@ function create(_, vnode) {
   attachData.placeholder = placeholder;
 }
 
-module.exports = function(target, vnode) {
+export = function attachTo(target: Element, vnode: VNode): VNode {
   if (vnode.data === undefined) vnode.data = {};
   if (vnode.data.hook === undefined) vnode.data.hook = {};
   var data = vnode.data;
diff --git a/src/htmldomapi.ts b/src/htmldomapi.ts
new file mode 100644
index 0000000..feb1d9a
--- /dev/null
+++ b/src/htmldomapi.ts
@@ -0,0 +1,54 @@
+import {DOMAPI} from './interfaces';
+
+function createElement(tagName: any): HTMLElement {
+  return document.createElement(tagName);
+}
+
+function createElementNS(namespaceURI: string, qualifiedName: string): Element {
+  return document.createElementNS(namespaceURI, qualifiedName);
+}
+
+function createTextNode(text: string): Text {
+  return document.createTextNode(text);
+}
+
+function insertBefore(parentNode: Node, newNode: Node, referenceNode: Node | null): void {
+  parentNode.insertBefore(newNode, referenceNode);
+}
+
+function removeChild(node: Node, child: Node): void {
+  node.removeChild(child);
+}
+
+function appendChild(node: Node, child: Node): void {
+  node.appendChild(child);
+}
+
+function parentNode(node: Node): HTMLElement {
+  return node.parentElement;
+}
+
+function nextSibling(node: Node): Node {
+  return node.nextSibling;
+}
+
+function tagName(elm: Element): string {
+  return elm.tagName;
+}
+
+function setTextContent(node: Node, text: string | null): void {
+  node.textContent = text;
+}
+
+export = {
+  createElement,
+  createElementNS,
+  createTextNode,
+  insertBefore,
+  removeChild,
+  appendChild,
+  parentNode,
+  nextSibling,
+  tagName,
+  setTextContent,
+} as DOMAPI;
\ No newline at end of file
diff --git a/src/interfaces.d.ts b/src/interfaces.d.ts
new file mode 100644
index 0000000..b3a6a15
--- /dev/null
+++ b/src/interfaces.d.ts
@@ -0,0 +1,87 @@
+export interface VNodeData {
+  // modules - use any because Object type is useless
+  props?: any;
+  attrs?: any;
+  class?: any;
+  style?: any;
+  dataset?: any;
+  on?: any;
+  hero?: any;
+  attachData?: any;
+  [key: string]: any; // for any other 3rd party module
+  // end of modules
+  hook?: Hooks;
+  key?: string | number;
+  ns?: string; // for SVGs
+  fn?: () => VNode; // for thunks
+  args?: Array<any>; // for thunks
+}
+
+export interface VNode {
+  sel: string | undefined;
+  data: VNodeData | undefined;
+  children: Array<VNode | string> | undefined;
+  elm: Element | Text | undefined;
+  text: string | undefined;
+  key: string | number | undefined;
+}
+
+export interface ThunkData extends VNodeData {
+  fn: () => VNode;
+  args: Array<any>;
+}
+
+export interface Thunk extends VNode {
+  data: ThunkData;
+}
+
+export interface ThunkFn {
+  (sel: string, fn: Function, args: Array<any>): Thunk;
+  (sel: string, key: any, fn: Function, args: Array<any>): Thunk;
+}
+
+export type PreHook = () => any;
+export type InitHook = (vNode: VNode) => any;
+export type CreateHook = (emptyVNode: VNode, vNode: VNode) => any;
+export type InsertHook = (vNode: VNode) => any;
+export type PrePatchHook = (oldVNode: VNode, vNode: VNode) => any;
+export type UpdateHook = (oldVNode: VNode, vNode: VNode) => any;
+export type PostPatchHook = (oldVNode: VNode, vNode: VNode) => any;
+export type DestroyHook = (vNode: VNode) => any;
+export type RemoveHook = (vNode: VNode, removeCallback: () => void) => any;
+export type PostHook = () => any;
+
+export interface Hooks {
+  pre?: PreHook;
+  init?: InitHook;
+  create?: CreateHook;
+  insert?: InsertHook;
+  prepatch?: PrePatchHook;
+  update?: UpdateHook;
+  postpatch?: PostPatchHook;
+  destroy?: DestroyHook;
+  remove?: RemoveHook;
+  post?: PostHook;
+}
+
+export interface Module {
+  pre?: PreHook;
+  create?: CreateHook;
+  update?: UpdateHook;
+  destroy?: DestroyHook;
+  remove?: RemoveHook;
+  post?: PostHook;
+}
+
+export interface DOMAPI {
+  createElement: (tagName: any) => HTMLElement;
+  createElementNS: (namespaceURI: string, qualifiedName: string) => Element;
+  createTextNode: (text: string) => Text;
+  insertBefore: (parentNode: Node, newNode: Node, referenceNode: Node | null) => void;
+  removeChild: (node: Node, child: Node) => void;
+  appendChild: (node: Node, child: Node) => void;
+  parentNode: (node: Node) => HTMLElement;
+  nextSibling: (node: Node) => Node;
+  tagName: (elm: Element) => string;
+  setTextContent: (node: Node, text: string | null) => void;
+}
\ No newline at end of file
diff --git a/src/is.ts b/src/is.ts
new file mode 100644
index 0000000..d392e71
--- /dev/null
+++ b/src/is.ts
@@ -0,0 +1,6 @@
+export = {
+  array: Array.isArray,
+  primitive: function primitive(s: any): boolean {
+    return typeof s === 'string' || typeof s === 'number';
+  },
+};
diff --git a/modules/attributes.js b/src/modules/attributes.ts
old mode 100644
new mode 100755
similarity index 60%
rename from modules/attributes.js
rename to src/modules/attributes.ts
index e486bc7..ea3931c
--- a/modules/attributes.js
+++ b/src/modules/attributes.ts
@@ -1,22 +1,25 @@
-var NamespaceURIs = {
+import {VNode, VNodeData, Module} from '../interfaces';
+
+const NamespaceURIs = {
   "xlink": "http://www.w3.org/1999/xlink"
 };
 
-var booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare",
+const booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare",
                 "default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable",
                 "enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple",
                 "muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly",
                 "required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate",
                 "truespeed", "typemustmatch", "visible"];
 
-var booleanAttrsDict = Object.create(null);
-for(var i=0, len = booleanAttrs.length; i < len; i++) {
+const booleanAttrsDict = Object.create(null);
+for (let i = 0, len = booleanAttrs.length; i < len; i++) {
   booleanAttrsDict[booleanAttrs[i]] = true;
 }
 
-function updateAttrs(oldVnode, vnode) {
-  var key, cur, old, elm = vnode.elm,
-      oldAttrs = oldVnode.data.attrs, attrs = vnode.data.attrs, namespaceSplit;
+function updateAttrs(oldVnode: VNode, vnode: VNode): void {
+  var key: string, cur: any, old: any, elm: Element = vnode.elm as Element,
+      oldAttrs = (oldVnode.data as VNodeData).attrs,
+      attrs = (vnode.data as VNodeData).attrs, namespaceSplit: Array<string>;
 
   if (!oldAttrs && !attrs) return;
   oldAttrs = oldAttrs || {};
@@ -27,12 +30,12 @@ function updateAttrs(oldVnode, vnode) {
     cur = attrs[key];
     old = oldAttrs[key];
     if (old !== cur) {
-      if(!cur && booleanAttrsDict[key])
+      if (!cur && booleanAttrsDict[key])
         elm.removeAttribute(key);
       else {
         namespaceSplit = key.split(":");
-        if(namespaceSplit.length > 1 && NamespaceURIs.hasOwnProperty(namespaceSplit[0]))
-          elm.setAttributeNS(NamespaceURIs[namespaceSplit[0]], key, cur);
+        if (namespaceSplit.length > 1 && NamespaceURIs.hasOwnProperty(namespaceSplit[0]))
+          elm.setAttributeNS((NamespaceURIs as any)[namespaceSplit[0]], key, cur);
         else
           elm.setAttribute(key, cur);
       }
@@ -48,4 +51,4 @@ function updateAttrs(oldVnode, vnode) {
   }
 }
 
-module.exports = {create: updateAttrs, update: updateAttrs};
+export = {create: updateAttrs, update: updateAttrs} as Module;
diff --git a/src/modules/class.ts b/src/modules/class.ts
new file mode 100755
index 0000000..64399e8
--- /dev/null
+++ b/src/modules/class.ts
@@ -0,0 +1,25 @@
+import {VNode, VNodeData, Module} from '../interfaces';
+
+function updateClass(oldVnode: VNode, vnode: VNode): void {
+  var cur: any, name: string, elm: Element = vnode.elm as Element,
+      oldClass = (oldVnode.data as VNodeData).class,
+      klass = (vnode.data as VNodeData).class;
+
+  if (!oldClass && !klass) return;
+  oldClass = oldClass || {};
+  klass = klass || {};
+
+  for (name in oldClass) {
+    if (!klass[name]) {
+      elm.classList.remove(name);
+    }
+  }
+  for (name in klass) {
+    cur = klass[name];
+    if (cur !== oldClass[name]) {
+      (elm.classList as any)[cur ? 'add' : 'remove'](name);
+    }
+  }
+}
+
+export = {create: updateClass, update: updateClass} as Module;
diff --git a/src/modules/dataset.ts b/src/modules/dataset.ts
new file mode 100755
index 0000000..6867d7e
--- /dev/null
+++ b/src/modules/dataset.ts
@@ -0,0 +1,25 @@
+import {VNode, VNodeData, Module} from '../interfaces';
+
+function updateDataset(oldVnode: VNode, vnode: VNode): void {
+  var elm: HTMLElement = vnode.elm as HTMLElement,
+    oldDataset = (oldVnode.data as VNodeData).dataset,
+    dataset = (vnode.data as VNodeData).dataset,
+    key: string;
+
+  if (!oldDataset && !dataset) return;
+  oldDataset = oldDataset || {};
+  dataset = dataset || {};
+
+  for (key in oldDataset) {
+    if (!dataset[key]) {
+      delete elm.dataset[key];
+    }
+  }
+  for (key in dataset) {
+    if (oldDataset[key] !== dataset[key]) {
+      elm.dataset[key] = dataset[key];
+    }
+  }
+}
+
+export = {create: updateDataset, update: updateDataset} as Module;
diff --git a/modules/eventlisteners.js b/src/modules/eventlisteners.ts
old mode 100644
new mode 100755
similarity index 74%
rename from modules/eventlisteners.js
rename to src/modules/eventlisteners.ts
index 34ad98d..036ac79
--- a/modules/eventlisteners.js
+++ b/src/modules/eventlisteners.ts
@@ -1,4 +1,6 @@
-function invokeHandler(handler, vnode, event) {
+import {VNode, VNodeData, Module} from '../interfaces';
+
+function invokeHandler(handler: any, vnode?: VNode, event?: Event): void {
   if (typeof handler === "function") {
     // call function handler
     handler.call(vnode, event, vnode);
@@ -23,9 +25,9 @@ function invokeHandler(handler, vnode, event) {
   }
 }
 
-function handleEvent(event, vnode) {
+function handleEvent(event: Event, vnode: VNode) {
   var name = event.type,
-      on = vnode.data.on;
+      on = (vnode.data as VNodeData).on;
 
   // call event handler(s) if exists
   if (on && on[name]) {
@@ -34,18 +36,18 @@ function handleEvent(event, vnode) {
 }
 
 function createListener() {
-  return function handler(event) {
-    handleEvent(event, handler.vnode);
+  return function handler(event: Event) {
+    handleEvent(event, (handler as any).vnode);
   }
 }
 
-function updateEventListeners(oldVnode, vnode) {
-  var oldOn = oldVnode.data.on,
-      oldListener = oldVnode.listener,
-      oldElm = oldVnode.elm,
-      on = vnode && vnode.data.on,
-      elm = vnode && vnode.elm,
-      name;
+function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
+  var oldOn = (oldVnode.data as VNodeData).on,
+      oldListener = (oldVnode as any).listener,
+      oldElm: Element = oldVnode.elm as Element,
+      on = vnode && (vnode.data as VNodeData).on,
+      elm: Element = (vnode && vnode.elm) as Element,
+      name: string;
 
   // optimization for reused immutable handlers
   if (oldOn === on) {
@@ -73,7 +75,7 @@ function updateEventListeners(oldVnode, vnode) {
   // add new listeners which has not already attached
   if (on) {
     // reuse existing listener or create new
-    var listener = vnode.listener = oldVnode.listener || createListener();
+    var listener = (vnode as any).listener = (oldVnode as any).listener || createListener();
     // update vnode for listener
     listener.vnode = vnode;
 
@@ -94,8 +96,8 @@ function updateEventListeners(oldVnode, vnode) {
   }
 }
 
-module.exports = {
+export = {
   create: updateEventListeners,
   update: updateEventListeners,
   destroy: updateEventListeners
-};
+} as Module;
diff --git a/modules/hero.js b/src/modules/hero.ts
old mode 100644
new mode 100755
similarity index 55%
rename from modules/hero.js
rename to src/modules/hero.ts
index b8b3159..ad9f7c3
--- a/modules/hero.js
+++ b/src/modules/hero.ts
@@ -1,12 +1,14 @@
+import {VNode, VNodeData, Module} from '../interfaces';
+
 var raf = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout;
-var nextFrame = function(fn) { raf(function() { raf(fn); }); };
+var nextFrame = function(fn: any) { raf(function() { raf(fn); }); };
 
-function setNextFrame(obj, prop, val) {
+function setNextFrame(obj: any, prop: string, val: any): void {
   nextFrame(function() { obj[prop] = val; });
 }
 
-function getTextNodeRect(textNode) {
-  var rect;
+function getTextNodeRect(textNode: Text): ClientRect | undefined {
+  var rect: ClientRect | undefined;
   if (document.createRange) {
     var range = document.createRange();
     range.selectNodeContents(textNode);
@@ -17,7 +19,9 @@ function getTextNodeRect(textNode) {
   return rect;
 }
 
-function calcTransformOrigin(isTextNode, textRect, boundingRect) {
+function calcTransformOrigin(isTextNode: boolean,
+                             textRect: ClientRect | undefined,
+                             boundingRect: ClientRect): string {
   if (isTextNode) {
     if (textRect) {
       //calculate pixels to center of text from left edge of bounding box
@@ -29,73 +33,78 @@ function calcTransformOrigin(isTextNode, textRect, boundingRect) {
   return '0 0'; //top left
 }
 
-function getTextDx(oldTextRect, newTextRect) {
+function getTextDx(oldTextRect: ClientRect | undefined,
+                   newTextRect: ClientRect | undefined): number {
   if (oldTextRect && newTextRect) {
     return ((oldTextRect.left + oldTextRect.width/2) - (newTextRect.left + newTextRect.width/2));
   }
   return 0;
 }
-function getTextDy(oldTextRect, newTextRect) {
+function getTextDy(oldTextRect: ClientRect | undefined,
+                   newTextRect: ClientRect | undefined): number {
   if (oldTextRect && newTextRect) {
     return ((oldTextRect.top + oldTextRect.height/2) - (newTextRect.top + newTextRect.height/2));
   }
   return 0;
 }
 
-function isTextElement(elm) {
+function isTextElement(elm: Element | Text): elm is Text {
   return elm.childNodes.length === 1 && elm.childNodes[0].nodeType === 3;
 }
 
-var removed, created;
+var removed: any, created: any;
 
-function pre(oldVnode, vnode) {
+function pre() {
   removed = {};
   created = [];
 }
 
-function create(oldVnode, vnode) {
-  var hero = vnode.data.hero;
+function create(oldVnode: VNode, vnode: VNode): void {
+  var hero = (vnode.data as VNodeData).hero;
   if (hero && hero.id) {
     created.push(hero.id);
     created.push(vnode);
   }
 }
 
-function destroy(vnode) {
-  var hero = vnode.data.hero;
+function destroy(vnode: VNode): void {
+  var hero = (vnode.data as VNodeData).hero;
   if (hero && hero.id) {
     var elm = vnode.elm;
-    vnode.isTextNode = isTextElement(elm); //is this a text node?
-    vnode.boundingRect = elm.getBoundingClientRect(); //save the bounding rectangle to a new property on the vnode
-    vnode.textRect = vnode.isTextNode ? getTextNodeRect(elm.childNodes[0]) : null; //save bounding rect of inner text node
-    var computedStyle = window.getComputedStyle(elm, null); //get current styles (includes inherited properties)
-    vnode.savedStyle = JSON.parse(JSON.stringify(computedStyle)); //save a copy of computed style values
+    (vnode as any).isTextNode = isTextElement(elm as Element | Text); //is this a text node?
+    (vnode as any).boundingRect = (elm as Element).getBoundingClientRect(); //save the bounding rectangle to a new property on the vnode
+    (vnode as any).textRect = (vnode as any).isTextNode ? getTextNodeRect((elm as Element).childNodes[0] as Text) : null; //save bounding rect of inner text node
+    var computedStyle = window.getComputedStyle(elm as Element, void 0); //get current styles (includes inherited properties)
+    (vnode as any).savedStyle = JSON.parse(JSON.stringify(computedStyle)); //save a copy of computed style values
     removed[hero.id] = vnode;
   }
 }
 
 function post() {
-  var i, id, newElm, oldVnode, oldElm, hRatio, wRatio,
-      oldRect, newRect, dx, dy, origTransform, origTransition,
-      newStyle, oldStyle, newComputedStyle, isTextNode,
-      newTextRect, oldTextRect;
+  var i: number, id: any, newElm: Element, oldVnode: VNode, oldElm: Element,
+      hRatio: number, wRatio: number,
+      oldRect: ClientRect, newRect: ClientRect, dx: number, dy: number,
+      origTransform: string | null, origTransition: string | null,
+      newStyle: CSSStyleDeclaration, oldStyle: CSSStyleDeclaration,
+      newComputedStyle: CSSStyleDeclaration, isTextNode: boolean,
+      newTextRect: ClientRect | undefined, oldTextRect: ClientRect | undefined;
   for (i = 0; i < created.length; i += 2) {
     id = created[i];
     newElm = created[i+1].elm;
     oldVnode = removed[id];
     if (oldVnode) {
-      isTextNode = oldVnode.isTextNode && isTextElement(newElm); //Are old & new both text?
-      newStyle = newElm.style;
-      newComputedStyle = window.getComputedStyle(newElm, null); //get full computed style for new element
-      oldElm = oldVnode.elm;
-      oldStyle = oldElm.style;
+      isTextNode = (oldVnode as any).isTextNode && isTextElement(newElm); //Are old & new both text?
+      newStyle = (newElm as HTMLElement).style;
+      newComputedStyle = window.getComputedStyle(newElm, void 0); //get full computed style for new element
+      oldElm = oldVnode.elm as Element;
+      oldStyle = (oldElm as HTMLElement).style;
       //Overall element bounding boxes
       newRect = newElm.getBoundingClientRect();
-      oldRect = oldVnode.boundingRect; //previously saved bounding rect
+      oldRect = (oldVnode as any).boundingRect; //previously saved bounding rect
       //Text node bounding boxes & distances
       if (isTextNode) {
-        newTextRect = getTextNodeRect(newElm.childNodes[0]);
-        oldTextRect = oldVnode.textRect;
+        newTextRect = getTextNodeRect(newElm.childNodes[0] as Text);
+        oldTextRect = (oldVnode as any).textRect;
         dx = getTextDx(oldTextRect, newTextRect);
         dy = getTextDy(oldTextRect, newTextRect);
       } else {
@@ -119,13 +128,13 @@ function post() {
       setNextFrame(newStyle, 'transform', origTransform);
       setNextFrame(newStyle, 'opacity', '1');
       // Animate old element
-      for (var key in oldVnode.savedStyle) { //re-apply saved inherited properties
-        if (parseInt(key) != key) {
+      for (var key in (oldVnode as any).savedStyle) { //re-apply saved inherited properties
+        if (parseInt(key) != key as any as number) {
           var ms = key.substring(0,2) === 'ms';
           var moz = key.substring(0,3) === 'moz';
           var webkit = key.substring(0,6) === 'webkit';
-      	  if (!ms && !moz && !webkit) //ignore prefixed style properties
-        	  oldStyle[key] = oldVnode.savedStyle[key];
+          if (!ms && !moz && !webkit) //ignore prefixed style properties
+            (oldStyle as any)[key] = (oldVnode as any).savedStyle[key];
         }
       }
       oldStyle.position = 'absolute';
@@ -133,20 +142,20 @@ function post() {
       oldStyle.left = oldRect.left + 'px';
       oldStyle.width = oldRect.width + 'px'; //Needed for elements who were sized relative to their parents
       oldStyle.height = oldRect.height + 'px'; //Needed for elements who were sized relative to their parents
-      oldStyle.margin = 0; //Margin on hero element leads to incorrect positioning
+      oldStyle.margin = '0'; //Margin on hero element leads to incorrect positioning
       oldStyle.transformOrigin = calcTransformOrigin(isTextNode, oldTextRect, oldRect);
       oldStyle.transform = '';
       oldStyle.opacity = '1';
       document.body.appendChild(oldElm);
       setNextFrame(oldStyle, 'transform', 'translate('+ -dx +'px, '+ -dy +'px) scale('+wRatio+', '+hRatio+')'); //scale must be on far right for translate to be correct
       setNextFrame(oldStyle, 'opacity', '0');
-      oldElm.addEventListener('transitionend', function(ev) {
+      oldElm.addEventListener('transitionend', function (ev: TransitionEvent) {
         if (ev.propertyName === 'transform')
-          document.body.removeChild(ev.target);
+          document.body.removeChild(ev.target as Node);
       });
     }
   }
   removed = created = undefined;
 }
 
-module.exports = {pre: pre, create: create, destroy: destroy, post: post};
+export = {pre: pre, create: create, destroy: destroy, post: post} as Module;
diff --git a/src/modules/props.ts b/src/modules/props.ts
new file mode 100755
index 0000000..f6b3a89
--- /dev/null
+++ b/src/modules/props.ts
@@ -0,0 +1,26 @@
+import {VNode, VNodeData, Module} from '../interfaces';
+
+function updateProps(oldVnode: VNode, vnode: VNode): void {
+  var key: string, cur: any, old: any, elm = vnode.elm,
+      oldProps = (oldVnode.data as VNodeData).props,
+      props = (vnode.data as VNodeData).props;
+
+  if (!oldProps && !props) return;
+  oldProps = oldProps || {};
+  props = props || {};
+
+  for (key in oldProps) {
+    if (!props[key]) {
+      delete (elm as any)[key];
+    }
+  }
+  for (key in props) {
+    cur = props[key];
+    old = oldProps[key];
+    if (old !== cur && (key !== 'value' || (elm as any)[key] !== cur)) {
+      (elm as any)[key] = cur;
+    }
+  }
+}
+
+export = {create: updateProps, update: updateProps} as Module;
diff --git a/src/modules/style.ts b/src/modules/style.ts
new file mode 100755
index 0000000..fc601f3
--- /dev/null
+++ b/src/modules/style.ts
@@ -0,0 +1,76 @@
+import {VNode, VNodeData, Module} from '../interfaces';
+
+var raf = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout;
+var nextFrame = function(fn: any) { raf(function() { raf(fn); }); };
+
+function setNextFrame(obj: any, prop: string, val: any): void {
+  nextFrame(function() { obj[prop] = val; });
+}
+
+function updateStyle(oldVnode: VNode, vnode: VNode): void {
+  var cur: any, name: string, elm = vnode.elm,
+      oldStyle = (oldVnode.data as VNodeData).style,
+      style = (vnode.data as VNodeData).style;
+
+  if (!oldStyle && !style) return;
+  oldStyle = oldStyle || {};
+  style = style || {};
+  var oldHasDel = 'delayed' in oldStyle;
+
+  for (name in oldStyle) {
+    if (!style[name]) {
+      (elm as any).style[name] = '';
+    }
+  }
+  for (name in style) {
+    cur = style[name];
+    if (name === 'delayed') {
+      for (name in style.delayed) {
+        cur = style.delayed[name];
+        if (!oldHasDel || cur !== oldStyle.delayed[name]) {
+          setNextFrame((elm as any).style, name, cur);
+        }
+      }
+    } else if (name !== 'remove' && cur !== oldStyle[name]) {
+      (elm as any).style[name] = cur;
+    }
+  }
+}
+
+function applyDestroyStyle(vnode: VNode): void {
+  var style: any, name: string, elm = vnode.elm, s = (vnode.data as VNodeData).style;
+  if (!s || !(style = s.destroy)) return;
+  for (name in style) {
+    (elm as any).style[name] = style[name];
+  }
+}
+
+function applyRemoveStyle(vnode: VNode, rm: () => void): void {
+  var s = (vnode.data as VNodeData).style;
+  if (!s || !s.remove) {
+    rm();
+    return;
+  }
+  var name: string, elm = vnode.elm, i = 0, compStyle: CSSStyleDeclaration,
+      style = s.remove, amount = 0, applied: Array<string> = [];
+  for (name in style) {
+    applied.push(name);
+    (elm as any).style[name] = style[name];
+  }
+  compStyle = getComputedStyle(elm as Element);
+  var props = (compStyle as any)['transition-property'].split(', ');
+  for (; i < props.length; ++i) {
+    if(applied.indexOf(props[i]) !== -1) amount++;
+  }
+  (elm as Element).addEventListener('transitionend', function (ev: TransitionEvent) {
+    if (ev.target === elm) --amount;
+    if (amount === 0) rm();
+  });
+}
+
+export = {
+  create: updateStyle,
+  update: updateStyle,
+  destroy: applyDestroyStyle,
+  remove: applyRemoveStyle
+} as Module;
diff --git a/src/snabbdom.bundle.ts b/src/snabbdom.bundle.ts
new file mode 100644
index 0000000..c7cd1ff
--- /dev/null
+++ b/src/snabbdom.bundle.ts
@@ -0,0 +1,16 @@
+import snabbdom = require('./snabbdom');
+import attributesModule = require('./modules/attributes'); // for setting attributes on DOM elements
+import classModule = require('./modules/class'); // makes it easy to toggle classes
+import propsModule = require('./modules/props'); // for setting properties on DOM elements
+import styleModule = require('./modules/style'); // handles styling on elements with support for animations
+import eventListenersModule = require('./modules/eventlisteners'); // attaches event listeners
+import h = require('./h'); // helper function for creating vnodes
+var patch = snabbdom.init([ // Init patch function with choosen modules
+  attributesModule,
+  classModule,
+  propsModule,
+  styleModule,
+  eventListenersModule
+]) as (oldVNode: any, vnode: any) => any;
+
+export = { patch, h: h as any };
\ No newline at end of file
diff --git a/src/snabbdom.ts b/src/snabbdom.ts
new file mode 100644
index 0000000..f268b03
--- /dev/null
+++ b/src/snabbdom.ts
@@ -0,0 +1,272 @@
+/* global require, module, document, Node */
+import {VNode, VNodeData, Hooks, DOMAPI} from './interfaces';
+import vnode = require('./vnode');
+import is = require('./is');
+import htmlDomApi = require('./htmldomapi');
+
+function isUndef(s: any): boolean { return s === undefined; }
+function isDef(s: any): boolean { return s !== undefined; }
+
+type VNodeQueue = Array<VNode>;
+
+const emptyNode = vnode('', {}, [], undefined, undefined);
+
+function sameVnode(vnode1: VNode, vnode2: VNode): boolean {
+  return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
+}
+
+function createKeyToOldIdx(children: Array<VNode>, beginIdx: number, endIdx: number): any {
+  let i: number, map: any = {}, key: any;
+  for (i = beginIdx; i <= endIdx; ++i) {
+    key = children[i].key;
+    if (isDef(key)) map[key] = i;
+  }
+  return map;
+}
+
+const hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];
+
+function init(modules: Array<Hooks>, domApi?: DOMAPI) {
+  let i: number, j: number, cbs: any = {};
+  let api: DOMAPI = domApi as DOMAPI;
+
+  if (isUndef(domApi)) api = htmlDomApi;
+
+  for (i = 0; i < hooks.length; ++i) {
+    cbs[hooks[i]] = [];
+    for (j = 0; j < modules.length; ++j) {
+      if ((modules[j] as any)[hooks[i]] !== undefined) cbs[hooks[i]].push((modules[j] as any)[hooks[i]]);
+    }
+  }
+
+  function emptyNodeAt(elm: Element) {
+    const id = elm.id ? '#' + elm.id : '';
+    const c = elm.className ? '.' + elm.className.split(' ').join('.') : '';
+    return vnode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
+  }
+
+  function createRmCb(childElm: Element | Text, listeners: number) {
+    return function rmCb() {
+      if (--listeners === 0) {
+        const parent = api.parentNode(childElm);
+        api.removeChild(parent, childElm);
+      }
+    };
+  }
+
+  function createElm(vnode: VNode, insertedVnodeQueue: VNodeQueue): Element | Text {
+    let i: any, data = vnode.data;
+    if (isDef(data)) {
+      if (isDef(i = (data as VNodeData).hook) && isDef(i = i.init)) {
+        i(vnode);
+        data = vnode.data;
+      }
+    }
+    let elm: Element | Text, children = vnode.children, sel = vnode.sel;
+    if (isDef(sel)) {
+      // Parse selector
+      const hashIdx = (sel as string).indexOf('#');
+      const dotIdx = (sel as string).indexOf('.', hashIdx);
+      const hash = hashIdx > 0 ? hashIdx : (sel as string).length;
+      const dot = dotIdx > 0 ? dotIdx : (sel as string).length;
+      const tag = hashIdx !== -1 || dotIdx !== -1 ? (sel as string).slice(0, Math.min(hash, dot)) : sel;
+      elm = vnode.elm = isDef(data) && isDef(i = (data as VNodeData).ns) ? api.createElementNS(i, tag as string)
+                                                          : api.createElement(tag);
+      if (hash < dot) elm.id = (sel as string).slice(hash + 1, dot);
+      if (dotIdx > 0) elm.className = (sel as string).slice(dot + 1).replace(/\./g, ' ');
+      if (is.array(children)) {
+        for (i = 0; i < children.length; ++i) {
+          api.appendChild(elm, createElm(children[i] as VNode, insertedVnodeQueue));
+        }
+      } else if (is.primitive(vnode.text)) {
+        api.appendChild(elm, api.createTextNode(vnode.text as string));
+      }
+      for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode);
+      i = (vnode.data as VNodeData).hook; // Reuse variable
+      if (isDef(i)) {
+        if (i.create) i.create(emptyNode, vnode);
+        if (i.insert) insertedVnodeQueue.push(vnode);
+      }
+    } else {
+      elm = vnode.elm = api.createTextNode(vnode.text as string);
+    }
+    return vnode.elm;
+  }
+
+  function addVnodes(parentElm: Node,
+                     before: Node | null,
+                     vnodes: Array<VNode>,
+                     startIdx: number,
+                     endIdx: number,
+                     insertedVnodeQueue: VNodeQueue) {
+    for (; startIdx <= endIdx; ++startIdx) {
+      api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
+    }
+  }
+
+  function invokeDestroyHook(vnode: VNode) {
+    let i: any, j: number, data = vnode.data;
+    if (isDef(data)) {
+      if (isDef(i = (data as VNodeData).hook) && isDef(i = i.destroy)) i(vnode);
+      for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode);
+      if (isDef(i = vnode.children)) {
+        for (j = 0; j < (vnode.children as Array<VNode>).length; ++j) {
+          invokeDestroyHook((vnode.children as Array<VNode>)[j]);
+        }
+      }
+    }
+  }
+
+  function removeVnodes(parentElm: Element,
+                        vnodes: Array<VNode>,
+                        startIdx: number,
+                        endIdx: number): void {
+    for (; startIdx <= endIdx; ++startIdx) {
+      let i: any, listeners: number, rm: () => void, ch = vnodes[startIdx];
+      if (isDef(ch)) {
+        if (isDef(ch.sel)) {
+          invokeDestroyHook(ch);
+          listeners = cbs.remove.length + 1;
+          rm = createRmCb(ch.elm as Element | Text, listeners);
+          for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm);
+          if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) {
+            i(ch, rm);
+          } else {
+            rm();
+          }
+        } else { // Text node
+          api.removeChild(parentElm, ch.elm as Element | Text);
+        }
+      }
+    }
+  }
+
+  function updateChildren(parentElm: Element,
+                          oldCh: Array<VNode>,
+                          newCh: Array<VNode>,
+                          insertedVnodeQueue: VNodeQueue) {
+    let oldStartIdx = 0, newStartIdx = 0;
+    let oldEndIdx = oldCh.length - 1;
+    let oldStartVnode = oldCh[0];
+    let oldEndVnode = oldCh[oldEndIdx];
+    let newEndIdx = newCh.length - 1;
+    let newStartVnode = newCh[0];
+    let newEndVnode = newCh[newEndIdx];
+    let oldKeyToIdx: any, idxInOld: number, elmToMove: any, before: any;
+
+    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
+      if (isUndef(oldStartVnode)) {
+        oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
+      } else if (isUndef(oldEndVnode)) {
+        oldEndVnode = oldCh[--oldEndIdx];
+      } else if (sameVnode(oldStartVnode, newStartVnode)) {
+        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
+        oldStartVnode = oldCh[++oldStartIdx];
+        newStartVnode = newCh[++newStartIdx];
+      } else if (sameVnode(oldEndVnode, newEndVnode)) {
+        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
+        oldEndVnode = oldCh[--oldEndIdx];
+        newEndVnode = newCh[--newEndIdx];
+      } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
+        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
+        api.insertBefore(parentElm, oldStartVnode.elm as Element, api.nextSibling(oldEndVnode.elm as Element));
+        oldStartVnode = oldCh[++oldStartIdx];
+        newEndVnode = newCh[--newEndIdx];
+      } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
+        patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
+        api.insertBefore(parentElm, oldEndVnode.elm as Element, oldStartVnode.elm as Element);
+        oldEndVnode = oldCh[--oldEndIdx];
+        newStartVnode = newCh[++newStartIdx];
+      } else {
+        if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
+        idxInOld = oldKeyToIdx[newStartVnode.key as string | number];
+        if (isUndef(idxInOld)) { // New element
+          api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm as Element);
+          newStartVnode = newCh[++newStartIdx];
+        } else {
+          elmToMove = oldCh[idxInOld];
+          patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
+          oldCh[idxInOld] = undefined as any;
+          api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm as Element);
+          newStartVnode = newCh[++newStartIdx];
+        }
+      }
+    }
+    if (oldStartIdx > oldEndIdx) {
+      before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm;
+      addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
+    } else if (newStartIdx > newEndIdx) {
+      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
+    }
+  }
+
+  function patchVnode(oldVnode: VNode, vnode: VNode, insertedVnodeQueue: VNodeQueue) {
+    let i: any, hook: any;
+    if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
+      i(oldVnode, vnode);
+    }
+    let elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children;
+    if (oldVnode === vnode) return;
+    if (!sameVnode(oldVnode, vnode)) {
+      const parentElm = api.parentNode(oldVnode.elm as Element);
+      elm = createElm(vnode, insertedVnodeQueue);
+      api.insertBefore(parentElm, elm, oldVnode.elm as Element);
+      removeVnodes(parentElm, [oldVnode], 0, 0);
+      return;
+    }
+    if (isDef(vnode.data)) {
+      for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
+      i = (vnode.data as VNodeData).hook;
+      if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode);
+    }
+    if (isUndef(vnode.text)) {
+      if (isDef(oldCh) && isDef(ch)) {
+        if (oldCh !== ch) updateChildren(elm as Element, oldCh as Array<VNode>, ch as Array<VNode>, insertedVnodeQueue);
+      } else if (isDef(ch)) {
+        if (isDef(oldVnode.text)) api.setTextContent(elm as Element, '');
+        addVnodes(elm as Element, null, ch as Array<VNode>, 0, (ch as Array<VNode>).length - 1, insertedVnodeQueue);
+      } else if (isDef(oldCh)) {
+        removeVnodes(elm as Element, oldCh as Array<VNode>, 0, (oldCh as Array<VNode>).length - 1);
+      } else if (isDef(oldVnode.text)) {
+        api.setTextContent(elm as Element, '');
+      }
+    } else if (oldVnode.text !== vnode.text) {
+      api.setTextContent(elm as Element, vnode.text as string);
+    }
+    if (isDef(hook) && isDef(i = hook.postpatch)) {
+      i(oldVnode, vnode);
+    }
+  }
+
+  return function patch(oldVnode: VNode | Element, vnode: VNode): VNode {
+    let i: number, elm: Element, parent: Element;
+    const insertedVnodeQueue: VNodeQueue = [];
+    for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();
+
+    if (isUndef((oldVnode as VNode).sel)) {
+      oldVnode = emptyNodeAt(oldVnode as Element);
+    }
+
+    if (sameVnode(oldVnode as VNode, vnode)) {
+      patchVnode(oldVnode as VNode, vnode, insertedVnodeQueue);
+    } else {
+      elm = (oldVnode as VNode).elm as Element;
+      parent = api.parentNode(elm);
+
+      createElm(vnode, insertedVnodeQueue);
+
+      if (parent !== null) {
+        api.insertBefore(parent, vnode.elm as Element, api.nextSibling(elm));
+        removeVnodes(parent, [oldVnode as VNode], 0, 0);
+      }
+    }
+
+    for (i = 0; i < insertedVnodeQueue.length; ++i) {
+      (((insertedVnodeQueue[i].data as VNodeData).hook as Hooks).insert as any)(insertedVnodeQueue[i]);
+    }
+    for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
+    return vnode;
+  };
+}
+
+export = {init: init};
\ No newline at end of file
diff --git a/src/thunk.ts b/src/thunk.ts
new file mode 100644
index 0000000..ce7edff
--- /dev/null
+++ b/src/thunk.ts
@@ -0,0 +1,49 @@
+import {VNode, VNodeData, ThunkFn} from './interfaces';
+import h = require('./h');
+
+function copyToThunk(vnode: VNode, thunk: VNode): void {
+  thunk.elm = vnode.elm;
+  (vnode.data as VNodeData).fn = (thunk.data as VNodeData).fn;
+  (vnode.data as VNodeData).args = (thunk.data as VNodeData).args;
+  thunk.data = vnode.data;
+  thunk.children = vnode.children;
+  thunk.text = vnode.text;
+  thunk.elm = vnode.elm;
+}
+
+function init(thunk: VNode): void {
+  const cur = thunk.data as VNodeData;
+  const vnode = (cur.fn as any).apply(undefined, cur.args);
+  copyToThunk(vnode, thunk);
+}
+
+function prepatch(oldVnode: VNode, thunk: VNode): void {
+  let i: number, old = oldVnode.data as VNodeData, cur = thunk.data as VNodeData;
+  const oldArgs = old.args, args = cur.args;
+  if (old.fn !== cur.fn || (oldArgs as any).length !== (args as any).length) {
+    copyToThunk((cur.fn as any).apply(undefined, args), thunk);
+  }
+  for (i = 0; i < (args as any).length; ++i) {
+    if ((oldArgs as any)[i] !== (args as any)[i]) {
+      copyToThunk((cur.fn as any).apply(undefined, args), thunk);
+      return;
+    }
+  }
+  copyToThunk(oldVnode, thunk);
+}
+
+function thunk(sel: string, key?: any, fn?: any, args?: any): VNode {
+  if (args === undefined) {
+    args = fn;
+    fn = key;
+    key = undefined;
+  }
+  return h(sel, {
+    key: key,
+    hook: {init: init, prepatch: prepatch},
+    fn: fn,
+    args: args
+  });
+};
+
+export = thunk as ThunkFn;
\ No newline at end of file
diff --git a/src/vnode.ts b/src/vnode.ts
new file mode 100644
index 0000000..1362ac9
--- /dev/null
+++ b/src/vnode.ts
@@ -0,0 +1,13 @@
+import {VNode} from './interfaces';
+
+function vnode(sel: string,
+               data: any | undefined,
+               children: Array<VNode | string> | undefined,
+               text: string | undefined,
+               elm: Element | Text | undefined): VNode {
+  let key = data === undefined ? undefined : data.key;
+  return {sel: sel, data: data, children: children,
+          text: text, elm: elm, key: key};
+}
+
+export = vnode;
diff --git a/thunk.js b/thunk.js
deleted file mode 100644
index 21bf7b1..0000000
--- a/thunk.js
+++ /dev/null
@@ -1,46 +0,0 @@
-var h = require('./h');
-
-function copyToThunk(vnode, thunk) {
-  thunk.elm = vnode.elm;
-  vnode.data.fn = thunk.data.fn;
-  vnode.data.args = thunk.data.args;
-  thunk.data = vnode.data;
-  thunk.children = vnode.children;
-  thunk.text = vnode.text;
-  thunk.elm = vnode.elm;
-}
-
-function init(thunk) {
-  var cur = thunk.data;
-  var vnode = cur.fn.apply(undefined, cur.args);
-  copyToThunk(vnode, thunk);
-}
-
-function prepatch(oldVnode, thunk) {
-  var i, old = oldVnode.data, cur = thunk.data, vnode;
-  var oldArgs = old.args, args = cur.args;
-  if (old.fn !== cur.fn || oldArgs.length !== args.length) {
-    copyToThunk(cur.fn.apply(undefined, args), thunk);
-  }
-  for (i = 0; i < args.length; ++i) {
-    if (oldArgs[i] !== args[i]) {
-      copyToThunk(cur.fn.apply(undefined, args), thunk);
-      return;
-    }
-  }
-  copyToThunk(oldVnode, thunk);
-}
-
-module.exports = function(sel, key, fn, args) {
-  if (args === undefined) {
-    args = fn;
-    fn = key;
-    key = undefined;
-  }
-  return h(sel, {
-    key: key,
-    hook: {init: init, prepatch: prepatch},
-    fn: fn,
-    args: args
-  });
-};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..e0ea1e9
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,35 @@
+{
+    "compilerOptions": {
+        "module": "commonjs",
+        "target": "ES5",
+        "outDir": "./",
+        "noImplicitAny": true,
+        "sourceMap": true,
+        "strictNullChecks": true,
+        "declaration": true,
+        "removeComments": false,
+        "noUnusedLocals": true,
+        "lib": [
+            "dom",
+            "es5"
+        ]
+    },
+    "files": [
+        "src/helpers/attachto.ts",
+        "src/modules/attributes.ts",
+        "src/modules/class.ts",
+        "src/modules/dataset.ts",
+        "src/modules/eventlisteners.ts",
+        "src/modules/hero.ts",
+        "src/modules/props.ts",
+        "src/modules/style.ts",
+        "src/h.ts",
+        "src/htmldomapi.ts",
+        "src/interfaces.d.ts",
+        "src/is.ts",
+        "src/snabbdom.bundle.ts",
+        "src/snabbdom.ts",
+        "src/thunk.ts",
+        "src/vnode.ts"
+    ]
+}
diff --git a/type-definitions/snabbdom.d.ts b/type-definitions/snabbdom.d.ts
deleted file mode 100644
index da51aa8..0000000
--- a/type-definitions/snabbdom.d.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-export interface VNodeData {
-  // modules - use any because Object type is useless
-  props?: any;
-  attrs?: any;
-  class?: any;
-  style?: any;
-  dataset?: any;
-  on?: any;
-  hero?: any;
-  // end of modules
-  hook?: Hooks;
-  key?: string | number;
-  ns?: string; // for SVGs
-  fn?: () => VNode; // for thunks
-  args?: Array<any>; // for thunks
-}
-
-export interface VNode {
-  sel: string;
-  data?: VNodeData;
-  children?: Array<VNode | string>;
-  elm?: Element | Text;
-  text?: string;
-  key?: string | number;
-}
-
-export interface ThunkData extends VNodeData {
-  fn: () => VNode;
-  args: Array<any>;
-}
-
-export interface Thunk extends VNode {
-  data: ThunkData;
-}
-
-export type PreHook = () => any;
-export type InitHook = (vNode: VNode) => any;
-export type CreateHook = (emptyVNode: VNode, vNode: VNode) => any;
-export type InsertHook = (vNode: VNode) => any;
-export type PrePatchHook = (oldVNode: VNode, vNode: VNode) => any;
-export type UpdateHook = (oldVNode: VNode, vNode: VNode) => any;
-export type PostPatchHook = (oldVNode: VNode, vNode: VNode) => any;
-export type DestroyHook = (vNode: VNode) => any;
-export type RemoveHook = (vNode: VNode, removeCallback: () => void) => any;
-export type PostHook = () => any;
-
-export interface Hooks {
-  pre?: PreHook;
-  init?: InitHook;
-  create?: CreateHook;
-  insert?: InsertHook;
-  prepatch?: PrePatchHook;
-  update?: UpdateHook;
-  postpatch?: PostPatchHook;
-  destroy?: DestroyHook;
-  remove?: RemoveHook;
-  post?: PostHook;
-}
-
-export interface Module {
-  pre?: PreHook;
-  create?: CreateHook;
-  update?: UpdateHook;
-  destroy?: DestroyHook;
-  remove?: RemoveHook;
-  post?: PostHook;
-}
-
-export interface SnabbdomAPI<T> {
-  createElement(tagName: string): T;
-  createElementNS(namespaceURI: string, qualifiedName: string): T;
-  createTextNode(text: string): T;
-  insertBefore(parentNode: T, newNode: T, referenceNode: T): void;
-  removeChild(node: T, child: T): void;
-  appendChild(node: T, child: T): void;
-  parentNode(node: T): T;
-  nextSibling(node: T): T;
-  tagName(node: T): string;
-  setTextContent(node: T, text: string): void;
-}
-
-declare module "snabbdom" {
-  export interface PatchFunction {
-    (oldVNode: VNode, vnode: VNode): VNode;
-  }
-
-  export function init(modules: Object, api?: SnabbdomAPI<any>): PatchFunction;
-}
-
-declare module "snabbdom/vnode" {
-  export default function vnode(sel: string,
-                                data: VNodeData,
-                                children: Array<VNode | string>,
-                                text: string,
-                                elm: any): VNode;
-}
-
-declare module "snabbdom/is" {
-  export function array(x: any): boolean;
-  export function primitive(x: any): boolean;
-}
-
-declare module "snabbdom/thunk" {
-  export default function thunk(sel: string,
-                                key: string,
-                                render: (...state: Array<any>) => VNode,
-                                ...state: Array<any>): Thunk;
-}
-
-declare module "snabbdom/htmldomapi" {
-  let api: SnabbdomAPI<Element>;
-  export = api;
-}
-
-declare module "snabbdom/modules/class" {
-  let ClassModule: Module;
-  export = ClassModule;
-}
-
-declare module "snabbdom/modules/props" {
-  let PropsModule: Module;
-  export = PropsModule;
-}
-
-declare module "snabbdom/modules/attributes" {
-  let AttrsModule: Module;
-  export = AttrsModule;
-}
-
-declare module "snabbdom/modules/eventlisteners" {
-  let EventsModule: Module;
-  export = EventsModule;
-}
-
-declare module "snabbdom/modules/hero" {
-  let HeroModule: Module;
-  export = HeroModule;
-}
-
-declare module "snabbdom/modules/style" {
-  let StyleModule: Module;
-  export = StyleModule;
-}
-
-declare module "snabbdom/modules/dataset" {
-  let DatasetModule: Module;
-  export = DatasetModule;
-}
diff --git a/vnode.js b/vnode.js
deleted file mode 100644
index 7057afd..0000000
--- a/vnode.js
+++ /dev/null
@@ -1,5 +0,0 @@
-module.exports = function(sel, data, children, text, elm) {
-  var key = data === undefined ? undefined : data.key;
-  return {sel: sel, data: data, children: children,
-          text: text, elm: elm, key: key};
-};