diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md
index 50e0d9db94..bf373ffa41 100644
--- a/bindings/wasm/README.md
+++ b/bindings/wasm/README.md
@@ -67,7 +67,7 @@ const client = identity.Client.fromConfig(config)
 
 // Publish the DID Document to the IOTA Tangle
 // The message can be viewed at https://explorer.iota.org/<mainnet|devnet>/transaction/<messageId>
-client.publishDocument(doc.toJSON())
+client.publishDocument(doc)
     .then((receipt) => {
         console.log("Tangle Message Receipt: ", receipt)
         console.log("Tangle Message Url:", doc.id.network.messageURL(receipt.messageId))
diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md
index 70ea91936e..4af8471601 100644
--- a/bindings/wasm/docs/api-reference.md
+++ b/bindings/wasm/docs/api-reference.md
@@ -27,6 +27,8 @@
 <dd></dd>
 <dt><a href="#Network">Network</a></dt>
 <dd></dd>
+<dt><a href="#Receipt">Receipt</a></dt>
+<dd></dd>
 <dt><a href="#Service">Service</a></dt>
 <dd></dd>
 <dt><a href="#Timestamp">Timestamp</a></dt>
@@ -67,12 +69,12 @@
     * [new Client()](#new_Client_new)
     * _instance_
         * [.network()](#Client+network) ⇒ [<code>Network</code>](#Network)
-        * [.publishDocument(document)](#Client+publishDocument) ⇒ <code>Promise.&lt;any&gt;</code>
-        * [.publishDiff(message_id, diff)](#Client+publishDiff) ⇒ <code>Promise.&lt;any&gt;</code>
-        * [.publishJSON(index, data)](#Client+publishJSON) ⇒ <code>Promise.&lt;any&gt;</code>
-        * [.resolve(did)](#Client+resolve) ⇒ <code>Promise.&lt;any&gt;</code>
-        * [.resolveHistory(did)](#Client+resolveHistory) ⇒ <code>Promise.&lt;any&gt;</code>
-        * [.resolveDiffHistory(document)](#Client+resolveDiffHistory) ⇒ <code>Promise.&lt;any&gt;</code>
+        * [.publishDocument(document)](#Client+publishDocument) ⇒ [<code>Promise.&lt;Receipt&gt;</code>](#Receipt)
+        * [.publishDiff(message_id, diff)](#Client+publishDiff) ⇒ [<code>Promise.&lt;Receipt&gt;</code>](#Receipt)
+        * [.publishJSON(index, data)](#Client+publishJSON) ⇒ [<code>Promise.&lt;Receipt&gt;</code>](#Receipt)
+        * [.resolve(did)](#Client+resolve) ⇒ [<code>Promise.&lt;Document&gt;</code>](#Document)
+        * [.resolveHistory(did)](#Client+resolveHistory) ⇒ [<code>Promise.&lt;DocumentHistory&gt;</code>](#DocumentHistory)
+        * [.resolveDiffHistory(document)](#Client+resolveDiffHistory) ⇒ [<code>Promise.&lt;DiffChainHistory&gt;</code>](#DiffChainHistory)
         * [.checkCredential(data)](#Client+checkCredential) ⇒ <code>Promise.&lt;any&gt;</code>
         * [.checkPresentation(data)](#Client+checkPresentation) ⇒ <code>Promise.&lt;any&gt;</code>
     * _static_
@@ -92,18 +94,18 @@ Returns the `Client` Tangle network.
 **Kind**: instance method of [<code>Client</code>](#Client)  
 <a name="Client+publishDocument"></a>
 
-### client.publishDocument(document) ⇒ <code>Promise.&lt;any&gt;</code>
+### client.publishDocument(document) ⇒ [<code>Promise.&lt;Receipt&gt;</code>](#Receipt)
 Publishes an `IotaDocument` to the Tangle.
 
 **Kind**: instance method of [<code>Client</code>](#Client)  
 
 | Param | Type |
 | --- | --- |
-| document | <code>any</code> | 
+| document | [<code>Document</code>](#Document) | 
 
 <a name="Client+publishDiff"></a>
 
-### client.publishDiff(message_id, diff) ⇒ <code>Promise.&lt;any&gt;</code>
+### client.publishDiff(message_id, diff) ⇒ [<code>Promise.&lt;Receipt&gt;</code>](#Receipt)
 Publishes a `DocumentDiff` to the Tangle.
 
 **Kind**: instance method of [<code>Client</code>](#Client)  
@@ -115,7 +117,7 @@ Publishes a `DocumentDiff` to the Tangle.
 
 <a name="Client+publishJSON"></a>
 
-### client.publishJSON(index, data) ⇒ <code>Promise.&lt;any&gt;</code>
+### client.publishJSON(index, data) ⇒ [<code>Promise.&lt;Receipt&gt;</code>](#Receipt)
 Publishes arbitrary JSON data to the specified index on the Tangle.
 
 **Kind**: instance method of [<code>Client</code>](#Client)  
@@ -127,7 +129,7 @@ Publishes arbitrary JSON data to the specified index on the Tangle.
 
 <a name="Client+resolve"></a>
 
-### client.resolve(did) ⇒ <code>Promise.&lt;any&gt;</code>
+### client.resolve(did) ⇒ [<code>Promise.&lt;Document&gt;</code>](#Document)
 **Kind**: instance method of [<code>Client</code>](#Client)  
 
 | Param | Type |
@@ -136,7 +138,7 @@ Publishes arbitrary JSON data to the specified index on the Tangle.
 
 <a name="Client+resolveHistory"></a>
 
-### client.resolveHistory(did) ⇒ <code>Promise.&lt;any&gt;</code>
+### client.resolveHistory(did) ⇒ [<code>Promise.&lt;DocumentHistory&gt;</code>](#DocumentHistory)
 Returns the message history of the given DID.
 
 **Kind**: instance method of [<code>Client</code>](#Client)  
@@ -147,7 +149,7 @@ Returns the message history of the given DID.
 
 <a name="Client+resolveDiffHistory"></a>
 
-### client.resolveDiffHistory(document) ⇒ <code>Promise.&lt;any&gt;</code>
+### client.resolveDiffHistory(document) ⇒ [<code>Promise.&lt;DiffChainHistory&gt;</code>](#DiffChainHistory)
 Returns the `DiffChainHistory` of a diff chain starting from a document on the
 integration chain.
 
@@ -608,24 +610,24 @@ Parses a `DIDUrl` from the input string.
 
 * [DiffChainHistory](#DiffChainHistory)
     * _instance_
-        * [.chainData()](#DiffChainHistory+chainData) ⇒ <code>Array.&lt;any&gt;</code>
-        * [.spam()](#DiffChainHistory+spam) ⇒ <code>Array.&lt;any&gt;</code>
+        * [.chainData()](#DiffChainHistory+chainData) ⇒ [<code>Array.&lt;DocumentDiff&gt;</code>](#DocumentDiff)
+        * [.spam()](#DiffChainHistory+spam) ⇒ <code>Array.&lt;string&gt;</code>
         * [.toJSON()](#DiffChainHistory+toJSON) ⇒ <code>any</code>
     * _static_
         * [.fromJSON(json)](#DiffChainHistory.fromJSON) ⇒ [<code>DiffChainHistory</code>](#DiffChainHistory)
 
 <a name="DiffChainHistory+chainData"></a>
 
-### diffChainHistory.chainData() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of the chain objects.
+### diffChainHistory.chainData() ⇒ [<code>Array.&lt;DocumentDiff&gt;</code>](#DocumentDiff)
+Returns an `Array` of the diff chain `DocumentDiffs`.
 
 NOTE: this clones the field.
 
 **Kind**: instance method of [<code>DiffChainHistory</code>](#DiffChainHistory)  
 <a name="DiffChainHistory+spam"></a>
 
-### diffChainHistory.spam() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of `MessageIds` as strings.
+### diffChainHistory.spam() ⇒ <code>Array.&lt;string&gt;</code>
+Returns an `Array` of `MessageIds` as strings.
 
 NOTE: this clones the field.
 
@@ -1161,26 +1163,26 @@ A DID Document's history and current state.
 
 * [DocumentHistory](#DocumentHistory)
     * _instance_
-        * [.integrationChainData()](#DocumentHistory+integrationChainData) ⇒ <code>Array.&lt;any&gt;</code>
-        * [.integrationChainSpam()](#DocumentHistory+integrationChainSpam) ⇒ <code>Array.&lt;any&gt;</code>
-        * [.diffChainData()](#DocumentHistory+diffChainData) ⇒ <code>Array.&lt;any&gt;</code>
-        * [.diffChainSpam()](#DocumentHistory+diffChainSpam) ⇒ <code>Array.&lt;any&gt;</code>
+        * [.integrationChainData()](#DocumentHistory+integrationChainData) ⇒ [<code>Array.&lt;Document&gt;</code>](#Document)
+        * [.integrationChainSpam()](#DocumentHistory+integrationChainSpam) ⇒ <code>Array.&lt;string&gt;</code>
+        * [.diffChainData()](#DocumentHistory+diffChainData) ⇒ [<code>Array.&lt;DocumentDiff&gt;</code>](#DocumentDiff)
+        * [.diffChainSpam()](#DocumentHistory+diffChainSpam) ⇒ <code>Array.&lt;string&gt;</code>
         * [.toJSON()](#DocumentHistory+toJSON) ⇒ <code>any</code>
     * _static_
         * [.fromJSON(json)](#DocumentHistory.fromJSON) ⇒ [<code>DocumentHistory</code>](#DocumentHistory)
 
 <a name="DocumentHistory+integrationChainData"></a>
 
-### documentHistory.integrationChainData() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of integration chain `Documents`.
+### documentHistory.integrationChainData() ⇒ [<code>Array.&lt;Document&gt;</code>](#Document)
+Returns an `Array` of integration chain `Documents`.
 
 NOTE: clones the data.
 
 **Kind**: instance method of [<code>DocumentHistory</code>](#DocumentHistory)  
 <a name="DocumentHistory+integrationChainSpam"></a>
 
-### documentHistory.integrationChainSpam() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of message id strings for "spam" messages on the same index
+### documentHistory.integrationChainSpam() ⇒ <code>Array.&lt;string&gt;</code>
+Returns an `Array` of message id strings for "spam" messages on the same index
 as the integration chain.
 
 NOTE: clones the data.
@@ -1188,16 +1190,16 @@ NOTE: clones the data.
 **Kind**: instance method of [<code>DocumentHistory</code>](#DocumentHistory)  
 <a name="DocumentHistory+diffChainData"></a>
 
-### documentHistory.diffChainData() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of diff chain `DocumentDiffs`.
+### documentHistory.diffChainData() ⇒ [<code>Array.&lt;DocumentDiff&gt;</code>](#DocumentDiff)
+Returns an `Array` of diff chain `DocumentDiffs`.
 
 NOTE: clones the data.
 
 **Kind**: instance method of [<code>DocumentHistory</code>](#DocumentHistory)  
 <a name="DocumentHistory+diffChainSpam"></a>
 
-### documentHistory.diffChainSpam() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of message id strings for "spam" messages on the same index
+### documentHistory.diffChainSpam() ⇒ <code>Array.&lt;string&gt;</code>
+Returns an `Array` of message id strings for "spam" messages on the same index
 as the diff chain.
 
 NOTE: clones the data.
@@ -1227,24 +1229,24 @@ Deserializes `DocumentHistory` from a JSON object.
 
 * [IntegrationChainHistory](#IntegrationChainHistory)
     * _instance_
-        * [.chainData()](#IntegrationChainHistory+chainData) ⇒ <code>Array.&lt;any&gt;</code>
-        * [.spam()](#IntegrationChainHistory+spam) ⇒ <code>Array.&lt;any&gt;</code>
+        * [.chainData()](#IntegrationChainHistory+chainData) ⇒ [<code>Array.&lt;Document&gt;</code>](#Document)
+        * [.spam()](#IntegrationChainHistory+spam) ⇒ <code>Array.&lt;string&gt;</code>
         * [.toJSON()](#IntegrationChainHistory+toJSON) ⇒ <code>any</code>
     * _static_
         * [.fromJSON(json)](#IntegrationChainHistory.fromJSON) ⇒ [<code>IntegrationChainHistory</code>](#IntegrationChainHistory)
 
 <a name="IntegrationChainHistory+chainData"></a>
 
-### integrationChainHistory.chainData() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of the chain objects.
+### integrationChainHistory.chainData() ⇒ [<code>Array.&lt;Document&gt;</code>](#Document)
+Returns an `Array` of the integration chain `Documents`.
 
 NOTE: this clones the field.
 
 **Kind**: instance method of [<code>IntegrationChainHistory</code>](#IntegrationChainHistory)  
 <a name="IntegrationChainHistory+spam"></a>
 
-### integrationChainHistory.spam() ⇒ <code>Array.&lt;any&gt;</code>
-Returns a `js_sys::Array` of `MessageIds` as strings.
+### integrationChainHistory.spam() ⇒ <code>Array.&lt;string&gt;</code>
+Returns an `Array` of `MessageIds` as strings.
 
 NOTE: this clones the field.
 
@@ -1506,6 +1508,62 @@ Parses the provided string to a `Network`.
 
 ### Network.devnet() ⇒ [<code>Network</code>](#Network)
 **Kind**: static method of [<code>Network</code>](#Network)  
+<a name="Receipt"></a>
+
+## Receipt
+**Kind**: global class  
+
+* [Receipt](#Receipt)
+    * _instance_
+        * [.network](#Receipt+network) ⇒ [<code>Network</code>](#Network)
+        * [.messageId](#Receipt+messageId) ⇒ <code>string</code>
+        * [.networkId](#Receipt+networkId) ⇒ <code>string</code>
+        * [.nonce](#Receipt+nonce) ⇒ <code>string</code>
+        * [.toJSON()](#Receipt+toJSON) ⇒ <code>any</code>
+    * _static_
+        * [.fromJSON(json)](#Receipt.fromJSON) ⇒ [<code>Receipt</code>](#Receipt)
+
+<a name="Receipt+network"></a>
+
+### receipt.network ⇒ [<code>Network</code>](#Network)
+Returns the associated IOTA Tangle `Network`.
+
+**Kind**: instance property of [<code>Receipt</code>](#Receipt)  
+<a name="Receipt+messageId"></a>
+
+### receipt.messageId ⇒ <code>string</code>
+Returns the message `id`.
+
+**Kind**: instance property of [<code>Receipt</code>](#Receipt)  
+<a name="Receipt+networkId"></a>
+
+### receipt.networkId ⇒ <code>string</code>
+Returns the message `network_id`.
+
+**Kind**: instance property of [<code>Receipt</code>](#Receipt)  
+<a name="Receipt+nonce"></a>
+
+### receipt.nonce ⇒ <code>string</code>
+Returns the message `nonce`.
+
+**Kind**: instance property of [<code>Receipt</code>](#Receipt)  
+<a name="Receipt+toJSON"></a>
+
+### receipt.toJSON() ⇒ <code>any</code>
+Serializes a `Receipt` as a JSON object.
+
+**Kind**: instance method of [<code>Receipt</code>](#Receipt)  
+<a name="Receipt.fromJSON"></a>
+
+### Receipt.fromJSON(json) ⇒ [<code>Receipt</code>](#Receipt)
+Deserializes a `Receipt` from a JSON object.
+
+**Kind**: static method of [<code>Receipt</code>](#Receipt)  
+
+| Param | Type |
+| --- | --- |
+| json | <code>any</code> | 
+
 <a name="Service"></a>
 
 ## Service
diff --git a/bindings/wasm/examples/src/create_did.js b/bindings/wasm/examples/src/create_did.js
index ef630ccb7b..d2f9556b74 100644
--- a/bindings/wasm/examples/src/create_did.js
+++ b/bindings/wasm/examples/src/create_did.js
@@ -29,7 +29,7 @@ async function createIdentity(clientConfig) {
     const client = Client.fromConfig(config);
 
     // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
-    const receipt = await client.publishDocument(doc.toJSON());
+    const receipt = await client.publishDocument(doc);
     doc.messageId = receipt.messageId;
 
     // Log the results.
diff --git a/bindings/wasm/examples/src/manipulate_did.js b/bindings/wasm/examples/src/manipulate_did.js
index b23d0606a3..99afbe41ca 100644
--- a/bindings/wasm/examples/src/manipulate_did.js
+++ b/bindings/wasm/examples/src/manipulate_did.js
@@ -51,7 +51,7 @@ async function manipulateIdentity(clientConfig) {
     doc.signSelf(key, doc.defaultSigningMethod().id.toString());
 
     // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
-    const updateReceipt = await client.publishDocument(doc.toJSON());
+    const updateReceipt = await client.publishDocument(doc);
 
     // Log the results.
     logExplorerUrl("Identity Update:", clientConfig.network.toString(), updateReceipt.messageId);
diff --git a/bindings/wasm/examples/src/merkle_key.js b/bindings/wasm/examples/src/merkle_key.js
index c863f1d235..5234c9bed9 100644
--- a/bindings/wasm/examples/src/merkle_key.js
+++ b/bindings/wasm/examples/src/merkle_key.js
@@ -46,7 +46,7 @@ async function merkleKey(clientConfig) {
 
     // Publish the Identity to the IOTA Network and log the results.
     // This may take a few seconds to complete proof-of-work.
-    const receipt = await client.publishDocument(issuer.doc.toJSON());
+    const receipt = await client.publishDocument(issuer.doc);
     logExplorerUrl("Identity Update:", clientConfig.network.toString(), receipt.messageId);
 
     // Prepare a credential subject indicating the degree earned by Alice
@@ -84,7 +84,7 @@ async function merkleKey(clientConfig) {
     issuer.doc.previousMessageId = receipt.messageId;
     issuer.doc.updated = Timestamp.nowUTC();
     issuer.doc.signSelf(issuer.key, issuer.doc.defaultSigningMethod().id.toString());
-    const nextReceipt = await client.publishDocument(issuer.doc.toJSON());
+    const nextReceipt = await client.publishDocument(issuer.doc);
     logExplorerUrl("Identity Update:", clientConfig.network.toString(), nextReceipt.messageId);
 
     // Check the verifiable credential is revoked
diff --git a/bindings/wasm/examples/src/private_tangle.js b/bindings/wasm/examples/src/private_tangle.js
index 07f86e7171..ad1d6b1622 100644
--- a/bindings/wasm/examples/src/private_tangle.js
+++ b/bindings/wasm/examples/src/private_tangle.js
@@ -39,7 +39,7 @@ async function createIdentityPrivateTangle(restURL, networkName) {
     const client = Client.fromConfig(config);
 
     // Publish the Identity to the IOTA Network, this may take a few seconds to complete Proof-of-Work.
-    const receipt = await client.publishDocument(doc.toJSON());
+    const receipt = await client.publishDocument(doc);
 
     // Make sure the DID can be resolved on the private tangle
     const resolved = await client.resolve(doc.id.toString());
diff --git a/bindings/wasm/examples/src/resolve_history.js b/bindings/wasm/examples/src/resolve_history.js
index 8e7235b0d3..460ef775f1 100644
--- a/bindings/wasm/examples/src/resolve_history.js
+++ b/bindings/wasm/examples/src/resolve_history.js
@@ -71,7 +71,7 @@ async function resolveHistory(clientConfig) {
 
     // Publish the updated DID Document to the Tangle, updating the integration chain.
     // This may take a few seconds to complete proof-of-work.
-    const intReceipt1 = await client.publishDocument(intDoc1.toJSON());
+    const intReceipt1 = await client.publishDocument(intDoc1);
 
     // Log the results.
     logExplorerUrl("Int. Chain Update (1):", clientConfig.network.toString(), intReceipt1.messageId);
@@ -175,7 +175,7 @@ async function resolveHistory(clientConfig) {
     intDoc2.previousMessageId = intReceipt1.messageId;
     intDoc2.updated = Timestamp.nowUTC();
     intDoc2.signSelf(key, intDoc2.defaultSigningMethod().id.toString());
-    const intReceipt2 = await client.publishDocument(intDoc2.toJSON());
+    const intReceipt2 = await client.publishDocument(intDoc2);
 
     // Log the results.
     logExplorerUrl("Int. Chain Update (2):", clientConfig.network.toString(), intReceipt2.messageId);
diff --git a/bindings/wasm/examples/src/revoke_vc.js b/bindings/wasm/examples/src/revoke_vc.js
index 8c8ea1db1e..3b98b9dbc8 100644
--- a/bindings/wasm/examples/src/revoke_vc.js
+++ b/bindings/wasm/examples/src/revoke_vc.js
@@ -37,7 +37,7 @@ async function revokeVC(clientConfig) {
     issuer.doc.updated = Timestamp.nowUTC();
     issuer.doc.signSelf(issuer.key, issuer.doc.defaultSigningMethod().id.toString());
     // This is an integration chain update, so we publish the full document.
-    const {messageId} = await client.publishDocument(issuer.doc.toJSON());
+    const {messageId} = await client.publishDocument(issuer.doc);
 
     // Log the resulting Identity update
     logExplorerUrl("Issuer Identity Update:", clientConfig.network.toString(), messageId);
diff --git a/bindings/wasm/src/chain/document_history.rs b/bindings/wasm/src/chain/document_history.rs
index b86d4095cc..5ba8f02777 100644
--- a/bindings/wasm/src/chain/document_history.rs
+++ b/bindings/wasm/src/chain/document_history.rs
@@ -6,6 +6,7 @@ use identity::iota::DocumentDiff;
 use identity::iota::DocumentHistory;
 use identity::iota::IotaDocument;
 use wasm_bindgen::prelude::*;
+use wasm_bindgen::JsCast;
 
 use crate::did::WasmDocument;
 use crate::did::WasmDocumentDiff;
@@ -17,13 +18,35 @@ use crate::error::WasmResult;
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct WasmDocumentHistory(DocumentHistory);
 
+// Workaround for Typescript type annotations on async function returns and arrays.
+#[wasm_bindgen]
+extern "C" {
+  #[wasm_bindgen(typescript_type = "Promise<DocumentHistory>")]
+  pub type PromiseDocumentHistory;
+
+  #[wasm_bindgen(typescript_type = "Promise<IntegrationChainHistory>")]
+  pub type PromiseIntegrationChainHistory;
+
+  #[wasm_bindgen(typescript_type = "Promise<DiffChainHistory>")]
+  pub type PromiseDiffChainHistory;
+
+  #[wasm_bindgen(typescript_type = "Array<string>")]
+  pub type ArrayString;
+
+  #[wasm_bindgen(typescript_type = "Array<Document>")]
+  pub type ArrayDocument;
+
+  #[wasm_bindgen(typescript_type = "Array<DocumentDiff>")]
+  pub type ArrayDocumentDiff;
+}
+
 #[wasm_bindgen(js_class = DocumentHistory)]
 impl WasmDocumentHistory {
-  /// Returns a `js_sys::Array` of integration chain `Documents`.
+  /// Returns an `Array` of integration chain `Documents`.
   ///
   /// NOTE: clones the data.
   #[wasm_bindgen(js_name = integrationChainData)]
-  pub fn integration_chain_data(&self) -> js_sys::Array {
+  pub fn integration_chain_data(&self) -> ArrayDocument {
     self
       .0
       .integration_chain_data
@@ -31,15 +54,16 @@ impl WasmDocumentHistory {
       .cloned()
       .map(WasmDocument::from)
       .map(JsValue::from)
-      .collect()
+      .collect::<js_sys::Array>()
+      .unchecked_into::<ArrayDocument>()
   }
 
-  /// Returns a `js_sys::Array` of message id strings for "spam" messages on the same index
+  /// Returns an `Array` of message id strings for "spam" messages on the same index
   /// as the integration chain.
   ///
   /// NOTE: clones the data.
   #[wasm_bindgen(js_name = integrationChainSpam)]
-  pub fn integration_chain_spam(&self) -> js_sys::Array {
+  pub fn integration_chain_spam(&self) -> ArrayString {
     self
       .0
       .integration_chain_spam
@@ -47,14 +71,15 @@ impl WasmDocumentHistory {
       .cloned()
       .map(|message_id| message_id.to_string())
       .map(JsValue::from)
-      .collect()
+      .collect::<js_sys::Array>()
+      .unchecked_into::<ArrayString>()
   }
 
-  /// Returns a `js_sys::Array` of diff chain `DocumentDiffs`.
+  /// Returns an `Array` of diff chain `DocumentDiffs`.
   ///
   /// NOTE: clones the data.
   #[wasm_bindgen(js_name = diffChainData)]
-  pub fn diff_chain_data(&self) -> js_sys::Array {
+  pub fn diff_chain_data(&self) -> ArrayDocumentDiff {
     self
       .0
       .diff_chain_data
@@ -62,15 +87,16 @@ impl WasmDocumentHistory {
       .cloned()
       .map(WasmDocumentDiff::from)
       .map(JsValue::from)
-      .collect()
+      .collect::<js_sys::Array>()
+      .unchecked_into::<ArrayDocumentDiff>()
   }
 
-  /// Returns a `js_sys::Array` of message id strings for "spam" messages on the same index
+  /// Returns an `Array` of message id strings for "spam" messages on the same index
   /// as the diff chain.
   ///
   /// NOTE: clones the data.
   #[wasm_bindgen(js_name = diffChainSpam)]
-  pub fn diff_chain_spam(&self) -> js_sys::Array {
+  pub fn diff_chain_spam(&self) -> ArrayString {
     self
       .0
       .diff_chain_spam
@@ -78,7 +104,8 @@ impl WasmDocumentHistory {
       .cloned()
       .map(|message_id| message_id.to_string())
       .map(JsValue::from)
-      .collect()
+      .collect::<js_sys::Array>()
+      .unchecked_into::<ArrayString>()
   }
 
   /// Serializes `DocumentHistory` as a JSON object.
@@ -108,30 +135,53 @@ pub struct IntegrationChainHistory(ChainHistory<IotaDocument>);
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct DiffChainHistory(ChainHistory<DocumentDiff>);
 
+#[wasm_bindgen]
+impl IntegrationChainHistory {
+  /// Returns an `Array` of the integration chain `Documents`.
+  ///
+  /// NOTE: this clones the field.
+  #[wasm_bindgen(js_name = chainData)]
+  pub fn chain_data(&self) -> ArrayDocument {
+    self
+      .0
+      .chain_data
+      .iter()
+      .cloned()
+      .map(WasmDocument::from)
+      .map(JsValue::from)
+      .collect::<js_sys::Array>()
+      .unchecked_into::<ArrayDocument>()
+  }
+}
+
+#[wasm_bindgen]
+impl DiffChainHistory {
+  /// Returns an `Array` of the diff chain `DocumentDiffs`.
+  ///
+  /// NOTE: this clones the field.
+  #[wasm_bindgen(js_name = chainData)]
+  pub fn chain_data(&self) -> ArrayDocumentDiff {
+    self
+      .0
+      .chain_data
+      .iter()
+      .cloned()
+      .map(WasmDocumentDiff::from)
+      .map(JsValue::from)
+      .collect::<js_sys::Array>()
+      .unchecked_into::<ArrayDocumentDiff>()
+  }
+}
+
 macro_rules! impl_wasm_chain_history {
   ($ident:ident, $ty:ty, $wasm_ty:ty) => {
     #[wasm_bindgen]
     impl $ident {
-      /// Returns a `js_sys::Array` of the chain objects.
-      ///
-      /// NOTE: this clones the field.
-      #[wasm_bindgen(js_name = chainData)]
-      pub fn chain_data(&self) -> js_sys::Array {
-        self
-          .0
-          .chain_data
-          .iter()
-          .cloned()
-          .map(<$wasm_ty>::from)
-          .map(JsValue::from)
-          .collect()
-      }
-
-      /// Returns a `js_sys::Array` of `MessageIds` as strings.
+      /// Returns an `Array` of `MessageIds` as strings.
       ///
       /// NOTE: this clones the field.
       #[wasm_bindgen]
-      pub fn spam(&self) -> js_sys::Array {
+      pub fn spam(&self) -> ArrayString {
         self
           .0
           .spam
@@ -139,7 +189,8 @@ macro_rules! impl_wasm_chain_history {
           .cloned()
           .map(|message_id| message_id.to_string())
           .map(JsValue::from)
-          .collect()
+          .collect::<js_sys::Array>()
+          .unchecked_into::<ArrayString>()
       }
 
       /// Serializes as a JSON object.
diff --git a/bindings/wasm/src/chain/mod.rs b/bindings/wasm/src/chain/mod.rs
index 3f287468bb..c566b05bb0 100644
--- a/bindings/wasm/src/chain/mod.rs
+++ b/bindings/wasm/src/chain/mod.rs
@@ -1,8 +1,6 @@
 // Copyright 2020-2021 IOTA Stiftung
 // SPDX-License-Identifier: Apache-2.0
 
-pub use document_history::DiffChainHistory;
-pub use document_history::IntegrationChainHistory;
-pub use document_history::WasmDocumentHistory;
+pub use document_history::*;
 
 mod document_history;
diff --git a/bindings/wasm/src/did/mod.rs b/bindings/wasm/src/did/mod.rs
index 255413f5cf..3691801aa3 100644
--- a/bindings/wasm/src/did/mod.rs
+++ b/bindings/wasm/src/did/mod.rs
@@ -1,14 +1,15 @@
 // Copyright 2020-2021 IOTA Stiftung
 // SPDX-License-Identifier: Apache-2.0
 
-mod wasm_did;
-mod wasm_did_url;
-mod wasm_document;
-mod wasm_document_diff;
-mod wasm_verification_method;
-
 pub use self::wasm_did::WasmDID;
 pub use self::wasm_did_url::WasmDIDUrl;
+pub use self::wasm_document::PromiseDocument;
 pub use self::wasm_document::WasmDocument;
 pub use self::wasm_document_diff::WasmDocumentDiff;
 pub use self::wasm_verification_method::WasmVerificationMethod;
+
+mod wasm_did;
+mod wasm_did_url;
+mod wasm_document;
+mod wasm_document_diff;
+mod wasm_verification_method;
diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs
index d4c6eaa2a0..21066f9443 100644
--- a/bindings/wasm/src/did/wasm_document.rs
+++ b/bindings/wasm/src/did/wasm_document.rs
@@ -39,6 +39,13 @@ use crate::service::Service;
 #[derive(Clone, Debug, PartialEq)]
 pub struct WasmDocument(pub(crate) IotaDocument);
 
+// Workaround for Typescript type annotations on async function returns.
+#[wasm_bindgen]
+extern "C" {
+  #[wasm_bindgen(typescript_type = "Promise<Document>")]
+  pub type PromiseDocument;
+}
+
 #[wasm_bindgen(js_class = Document)]
 impl WasmDocument {
   /// Creates a new DID Document from the given `KeyPair`, network, and verification method
diff --git a/bindings/wasm/src/tangle/client.rs b/bindings/wasm/src/tangle/client.rs
index 432591ba41..fefd72b596 100644
--- a/bindings/wasm/src/tangle/client.rs
+++ b/bindings/wasm/src/tangle/client.rs
@@ -17,16 +17,22 @@ use identity::iota::MessageId;
 use identity::iota::TangleResolve;
 use js_sys::Promise;
 use wasm_bindgen::prelude::*;
+use wasm_bindgen::JsCast;
 use wasm_bindgen_futures::future_to_promise;
 
 use crate::chain::DiffChainHistory;
+use crate::chain::PromiseDiffChainHistory;
+use crate::chain::PromiseDocumentHistory;
 use crate::chain::WasmDocumentHistory;
+use crate::did::PromiseDocument;
 use crate::did::WasmDocument;
 use crate::did::WasmDocumentDiff;
 use crate::error::Result;
 use crate::error::WasmResult;
 use crate::tangle::Config;
+use crate::tangle::PromiseReceipt;
 use crate::tangle::WasmNetwork;
+use crate::tangle::WasmReceipt;
 
 #[wasm_bindgen]
 #[derive(Debug)]
@@ -74,24 +80,26 @@ impl Client {
 
   /// Publishes an `IotaDocument` to the Tangle.
   #[wasm_bindgen(js_name = publishDocument)]
-  pub fn publish_document(&self, document: &JsValue) -> Result<Promise> {
-    let document: IotaDocument = document.into_serde().wasm_result()?;
+  pub fn publish_document(&self, document: &WasmDocument) -> Result<PromiseReceipt> {
+    let document: IotaDocument = document.0.clone();
     let client: Rc<IotaClient> = self.client.clone();
 
     let promise: Promise = future_to_promise(async move {
       client
         .publish_document(&document)
         .await
+        .map(WasmReceipt)
+        .map(Into::into)
         .wasm_result()
-        .and_then(|receipt| JsValue::from_serde(&receipt).wasm_result())
     });
 
-    Ok(promise)
+    // WARNING: this does not validate the return type. Check carefully.
+    Ok(promise.unchecked_into::<PromiseReceipt>())
   }
 
   /// Publishes a `DocumentDiff` to the Tangle.
   #[wasm_bindgen(js_name = publishDiff)]
-  pub fn publish_diff(&self, message_id: &str, diff: WasmDocumentDiff) -> Result<Promise> {
+  pub fn publish_diff(&self, message_id: &str, diff: WasmDocumentDiff) -> Result<PromiseReceipt> {
     let message: MessageId = MessageId::from_str(message_id).wasm_result()?;
     let client: Rc<IotaClient> = self.client.clone();
 
@@ -99,16 +107,18 @@ impl Client {
       client
         .publish_diff(&message, diff.deref())
         .await
+        .map(WasmReceipt)
+        .map(Into::into)
         .wasm_result()
-        .and_then(|receipt| JsValue::from_serde(&receipt).wasm_result())
     });
 
-    Ok(promise)
+    // WARNING: this does not validate the return type. Check carefully.
+    Ok(promise.unchecked_into::<PromiseReceipt>())
   }
 
   /// Publishes arbitrary JSON data to the specified index on the Tangle.
   #[wasm_bindgen(js_name = publishJSON)]
-  pub fn publish_json(&self, index: &str, data: &JsValue) -> Result<Promise> {
+  pub fn publish_json(&self, index: &str, data: &JsValue) -> Result<PromiseReceipt> {
     let client: Rc<IotaClient> = self.client.clone();
 
     let index = index.to_owned();
@@ -117,22 +127,17 @@ impl Client {
       client
         .publish_json(&index, &value)
         .await
+        .map(WasmReceipt)
+        .map(Into::into)
         .wasm_result()
-        .and_then(|receipt| JsValue::from_serde(&receipt).wasm_result())
     });
 
-    Ok(promise)
+    // WARNING: this does not validate the return type. Check carefully.
+    Ok(promise.unchecked_into::<PromiseReceipt>())
   }
 
   #[wasm_bindgen]
-  pub fn resolve(&self, did: &str) -> Result<Promise> {
-    #[derive(Serialize)]
-    pub struct DocWrapper<'a> {
-      document: &'a IotaDocument,
-      #[serde(rename = "messageId")]
-      message_id: &'a MessageId,
-    }
-
+  pub fn resolve(&self, did: &str) -> Result<PromiseDocument> {
     let client: Rc<IotaClient> = self.client.clone();
     let did: IotaDID = did.parse().wasm_result()?;
 
@@ -141,16 +146,17 @@ impl Client {
         .resolve(&did)
         .await
         .map(WasmDocument::from)
-        .map(JsValue::from)
+        .map(Into::into)
         .wasm_result()
     });
 
-    Ok(promise)
+    // WARNING: this does not validate the return type. Check carefully.
+    Ok(promise.unchecked_into::<PromiseDocument>())
   }
 
   /// Returns the message history of the given DID.
   #[wasm_bindgen(js_name = resolveHistory)]
-  pub fn resolve_history(&self, did: &str) -> Result<Promise> {
+  pub fn resolve_history(&self, did: &str) -> Result<PromiseDocumentHistory> {
     let did: IotaDID = did.parse().wasm_result()?;
     let client: Rc<IotaClient> = self.client.clone();
 
@@ -159,11 +165,12 @@ impl Client {
         .resolve_history(&did)
         .await
         .map(WasmDocumentHistory::from)
-        .map(JsValue::from)
+        .map(Into::into)
         .wasm_result()
     });
 
-    Ok(promise)
+    // WARNING: this does not validate the return type. Check carefully.
+    Ok(promise.unchecked_into::<PromiseDocumentHistory>())
   }
 
   /// Returns the `DiffChainHistory` of a diff chain starting from a document on the
@@ -172,7 +179,7 @@ impl Client {
   /// NOTE: the document must have been published to the tangle and have a valid message id and
   /// capability invocation method.
   #[wasm_bindgen(js_name = resolveDiffHistory)]
-  pub fn resolve_diff_history(&self, document: &WasmDocument) -> Result<Promise> {
+  pub fn resolve_diff_history(&self, document: &WasmDocument) -> Result<PromiseDiffChainHistory> {
     let client: Rc<IotaClient> = self.client.clone();
     let iota_document: IotaDocument = document.0.clone();
 
@@ -181,11 +188,12 @@ impl Client {
         .resolve_diff_history(&iota_document)
         .await
         .map(DiffChainHistory::from)
-        .map(JsValue::from)
+        .map(Into::into)
         .wasm_result()
     });
 
-    Ok(promise)
+    // WARNING: this does not validate the return type. Check carefully.
+    Ok(promise.unchecked_into::<PromiseDiffChainHistory>())
   }
 
   /// Validates a credential with the DID Document from the Tangle.
diff --git a/bindings/wasm/src/tangle/mod.rs b/bindings/wasm/src/tangle/mod.rs
index 4014aeef58..deead3ff35 100644
--- a/bindings/wasm/src/tangle/mod.rs
+++ b/bindings/wasm/src/tangle/mod.rs
@@ -5,8 +5,10 @@ pub use self::client::*;
 pub use self::config::*;
 pub use self::message::*;
 pub use self::network::*;
+pub use self::receipt::*;
 
 mod client;
 mod config;
 mod message;
 mod network;
+mod receipt;
diff --git a/bindings/wasm/src/tangle/network.rs b/bindings/wasm/src/tangle/network.rs
index c5fd1928f4..76d7f48efa 100644
--- a/bindings/wasm/src/tangle/network.rs
+++ b/bindings/wasm/src/tangle/network.rs
@@ -1,7 +1,7 @@
 // Copyright 2020-2021 IOTA Stiftung
 // SPDX-License-Identifier: Apache-2.0
 
-use identity::iota::Network as IotaNetwork;
+use identity::iota::Network;
 use wasm_bindgen::prelude::*;
 
 use crate::error::Result;
@@ -9,24 +9,24 @@ use crate::error::WasmResult;
 
 #[wasm_bindgen(js_name = Network)]
 #[derive(Clone, Debug)]
-pub struct WasmNetwork(IotaNetwork);
+pub struct WasmNetwork(Network);
 
 #[wasm_bindgen(js_class = Network)]
 impl WasmNetwork {
   /// Parses the provided string to a `Network`.
   #[wasm_bindgen]
   pub fn try_from_name(name: String) -> Result<WasmNetwork> {
-    IotaNetwork::try_from_name(name).map(Self).wasm_result()
+    Network::try_from_name(name).map(Self).wasm_result()
   }
 
   #[wasm_bindgen]
   pub fn mainnet() -> WasmNetwork {
-    Self(IotaNetwork::Mainnet)
+    Self(Network::Mainnet)
   }
 
   #[wasm_bindgen]
   pub fn devnet() -> WasmNetwork {
-    Self(IotaNetwork::Devnet)
+    Self(Network::Devnet)
   }
 
   /// Returns the node URL of the Tangle network.
@@ -56,18 +56,18 @@ impl WasmNetwork {
 
 impl Default for WasmNetwork {
   fn default() -> Self {
-    IotaNetwork::default().into()
+    Network::default().into()
   }
 }
 
-impl From<WasmNetwork> for IotaNetwork {
+impl From<WasmNetwork> for Network {
   fn from(other: WasmNetwork) -> Self {
     other.0
   }
 }
 
-impl From<IotaNetwork> for WasmNetwork {
-  fn from(other: IotaNetwork) -> Self {
+impl From<Network> for WasmNetwork {
+  fn from(other: Network) -> Self {
     Self(other)
   }
 }
diff --git a/bindings/wasm/src/tangle/receipt.rs b/bindings/wasm/src/tangle/receipt.rs
new file mode 100644
index 0000000000..0b4efb2c17
--- /dev/null
+++ b/bindings/wasm/src/tangle/receipt.rs
@@ -0,0 +1,73 @@
+// Copyright 2020-2021 IOTA Stiftung
+// SPDX-License-Identifier: Apache-2.0
+
+use identity::iota::Receipt;
+use wasm_bindgen::prelude::*;
+
+use crate::error::Result;
+use crate::error::WasmResult;
+use crate::tangle::WasmNetwork;
+
+#[wasm_bindgen(js_name = Receipt, inspectable)]
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct WasmReceipt(pub(crate) Receipt);
+
+// Workaround for Typescript type annotations on async function returns.
+#[wasm_bindgen]
+extern "C" {
+  #[wasm_bindgen(typescript_type = "Promise<Receipt>")]
+  pub type PromiseReceipt;
+}
+
+#[wasm_bindgen(js_class = Receipt)]
+impl WasmReceipt {
+  /// Returns the associated IOTA Tangle `Network`.
+  #[wasm_bindgen(getter)]
+  pub fn network(&self) -> WasmNetwork {
+    WasmNetwork::from(self.0.network())
+  }
+
+  /// Returns the message `id`.
+  #[wasm_bindgen(getter = messageId)]
+  pub fn message_id(&self) -> String {
+    self.0.message_id().to_string()
+  }
+
+  /// Returns the message `network_id`.
+  #[wasm_bindgen(getter = networkId)]
+  pub fn network_id(&self) -> String {
+    // NOTE: do not return u64 to avoid BigInt64Array/BigUint64Array compatibility issues.
+    self.0.network_id().to_string()
+  }
+
+  /// Returns the message `nonce`.
+  #[wasm_bindgen(getter)]
+  pub fn nonce(&self) -> String {
+    // NOTE: do not return u64 to avoid BigInt64Array/BigUint64Array compatibility issues.
+    self.0.nonce().to_string()
+  }
+
+  /// Serializes a `Receipt` as a JSON object.
+  #[wasm_bindgen(js_name = toJSON)]
+  pub fn to_json(&self) -> Result<JsValue> {
+    JsValue::from_serde(&self.0).wasm_result()
+  }
+
+  /// Deserializes a `Receipt` from a JSON object.
+  #[wasm_bindgen(js_name = fromJSON)]
+  pub fn from_json(json: &JsValue) -> Result<WasmReceipt> {
+    json.into_serde().map(Self).wasm_result()
+  }
+}
+
+impl From<Receipt> for WasmReceipt {
+  fn from(receipt: Receipt) -> Self {
+    Self(receipt)
+  }
+}
+
+impl From<WasmReceipt> for Receipt {
+  fn from(receipt: WasmReceipt) -> Self {
+    receipt.0
+  }
+}