diff --git a/bitcoind-tests/tests/setup/test_util.rs b/bitcoind-tests/tests/setup/test_util.rs
index ffffe661c..64ffa6f5a 100644
--- a/bitcoind-tests/tests/setup/test_util.rs
+++ b/bitcoind-tests/tests/setup/test_util.rs
@@ -26,7 +26,7 @@ use bitcoin::secp256k1;
 use miniscript::descriptor::{SinglePub, SinglePubKey};
 use miniscript::{
     bitcoin, hash256, Descriptor, DescriptorPublicKey, Error, Miniscript, ScriptContext,
-    TranslatePk, Translator,
+    Translator,
 };
 use rand::RngCore;
 use secp256k1::XOnlyPublicKey;
@@ -155,8 +155,11 @@ pub fn parse_insane_ms<Ctx: ScriptContext>(
 #[derive(Debug, Clone)]
 struct StrDescPubKeyTranslator<'a>(usize, &'a PubData);
 
-impl<'a> Translator<String, DescriptorPublicKey, ()> for StrDescPubKeyTranslator<'a> {
-    fn pk(&mut self, pk_str: &String) -> Result<DescriptorPublicKey, ()> {
+impl<'a> Translator<String> for StrDescPubKeyTranslator<'a> {
+    type TargetPk = DescriptorPublicKey;
+    type Error = core::convert::Infallible;
+
+    fn pk(&mut self, pk_str: &String) -> Result<Self::TargetPk, Self::Error> {
         let avail = !pk_str.ends_with('!');
         if avail {
             self.0 += 1;
@@ -181,22 +184,22 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrDescPubKeyTranslator
         }
     }
 
-    fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, ()> {
+    fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, Self::Error> {
         let sha = sha256::Hash::from_str(sha256).unwrap();
         Ok(sha)
     }
 
-    fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, ()> {
+    fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, Self::Error> {
         let hash256 = hash256::Hash::from_str(hash256).unwrap();
         Ok(hash256)
     }
 
-    fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, ()> {
+    fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, Self::Error> {
         let ripemd160 = ripemd160::Hash::from_str(ripemd160).unwrap();
         Ok(ripemd160)
     }
 
-    fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, ()> {
+    fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, Self::Error> {
         let hash160 = hash160::Hash::from_str(hash160).unwrap();
         Ok(hash160)
     }
@@ -208,8 +211,11 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrDescPubKeyTranslator
 #[derive(Debug, Clone)]
 struct StrTranslatorLoose<'a>(usize, &'a PubData);
 
-impl<'a> Translator<String, DescriptorPublicKey, ()> for StrTranslatorLoose<'a> {
-    fn pk(&mut self, pk_str: &String) -> Result<DescriptorPublicKey, ()> {
+impl<'a> Translator<String> for StrTranslatorLoose<'a> {
+    type TargetPk = DescriptorPublicKey;
+    type Error = core::convert::Infallible;
+
+    fn pk(&mut self, pk_str: &String) -> Result<Self::TargetPk, Self::Error> {
         let avail = !pk_str.ends_with('!');
         if avail {
             self.0 += 1;
@@ -238,22 +244,22 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrTranslatorLoose<'a>
         }
     }
 
-    fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, ()> {
+    fn sha256(&mut self, sha256: &String) -> Result<sha256::Hash, Self::Error> {
         let sha = sha256::Hash::from_str(sha256).unwrap();
         Ok(sha)
     }
 
-    fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, ()> {
+    fn hash256(&mut self, hash256: &String) -> Result<hash256::Hash, Self::Error> {
         let hash256 = hash256::Hash::from_str(hash256).unwrap();
         Ok(hash256)
     }
 
-    fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, ()> {
+    fn ripemd160(&mut self, ripemd160: &String) -> Result<ripemd160::Hash, Self::Error> {
         let ripemd160 = ripemd160::Hash::from_str(ripemd160).unwrap();
         Ok(ripemd160)
     }
 
-    fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, ()> {
+    fn hash160(&mut self, hash160: &String) -> Result<hash160::Hash, Self::Error> {
         let hash160 = hash160::Hash::from_str(hash160).unwrap();
         Ok(hash160)
     }
diff --git a/examples/big.rs b/examples/big.rs
index cf0bd099a..230d656b6 100644
--- a/examples/big.rs
+++ b/examples/big.rs
@@ -18,7 +18,7 @@ use miniscript::policy::{Concrete, Liftable};
 use miniscript::psbt::PsbtExt;
 use miniscript::{
     translate_hash_fail, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, MiniscriptKey,
-    TranslatePk, Translator,
+    Translator,
 };
 use secp256k1::Secp256k1;
 fn main() {
@@ -82,12 +82,15 @@ struct StrPkTranslator {
     pk_map: HashMap<String, XOnlyPublicKey>,
 }
 
-impl Translator<String, XOnlyPublicKey, ()> for StrPkTranslator {
-    fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, ()> {
+impl Translator<String> for StrPkTranslator {
+    type TargetPk = XOnlyPublicKey;
+    type Error = ();
+
+    fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, Self::Error> {
         self.pk_map.get(pk).copied().ok_or(())
     }
 
     // We don't need to implement these methods as we are not using them in the policy.
     // Fail if we encounter any hash fragments. See also translate_hash_clone! macro.
-    translate_hash_fail!(String, XOnlyPublicKey, ());
+    translate_hash_fail!(String, XOnlyPublicKey, Self::Error);
 }
diff --git a/examples/taproot.rs b/examples/taproot.rs
index 3ac20c4ec..1e04f7b60 100644
--- a/examples/taproot.rs
+++ b/examples/taproot.rs
@@ -8,7 +8,7 @@ use miniscript::bitcoin::secp256k1::rand;
 use miniscript::bitcoin::{Network, WitnessVersion};
 use miniscript::descriptor::DescriptorType;
 use miniscript::policy::Concrete;
-use miniscript::{translate_hash_fail, Descriptor, Miniscript, Tap, TranslatePk, Translator};
+use miniscript::{translate_hash_fail, Descriptor, Miniscript, Tap, Translator};
 
 // Refer to https://github.com/sanket1729/adv_btc_workshop/blob/master/workshop.md#creating-a-taproot-descriptor
 // for a detailed explanation of the policy and it's compilation
@@ -17,14 +17,17 @@ struct StrPkTranslator {
     pk_map: HashMap<String, XOnlyPublicKey>,
 }
 
-impl Translator<String, XOnlyPublicKey, ()> for StrPkTranslator {
-    fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, ()> {
+impl Translator<String> for StrPkTranslator {
+    type TargetPk = XOnlyPublicKey;
+    type Error = ();
+
+    fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, Self::Error> {
         self.pk_map.get(pk).copied().ok_or(())
     }
 
     // We don't need to implement these methods as we are not using them in the policy.
     // Fail if we encounter any hash fragments. See also translate_hash_clone! macro.
-    translate_hash_fail!(String, XOnlyPublicKey, ());
+    translate_hash_fail!(String, XOnlyPublicKey, Self::Error);
 }
 
 fn main() {
diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs
index 66be5d6d4..176137ad8 100644
--- a/src/descriptor/bare.rs
+++ b/src/descriptor/bare.rs
@@ -23,7 +23,7 @@ use crate::prelude::*;
 use crate::util::{varint_len, witness_to_scriptsig};
 use crate::{
     BareCtx, Error, ForEachKey, FromStrKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey,
-    TranslateErr, TranslatePk, Translator,
+    TranslateErr, Translator,
 };
 
 /// Create a Bare Descriptor. That is descriptor that is
@@ -92,6 +92,14 @@ impl<Pk: MiniscriptKey> Bare<Pk> {
         let scriptsig_len = self.ms.max_satisfaction_size()?;
         Ok(4 * (varint_len(scriptsig_len) + scriptsig_len))
     }
+
+    /// Converts the keys in the script from one type to another.
+    pub fn translate_pk<T>(&self, t: &mut T) -> Result<Bare<T::TargetPk>, TranslateErr<T::Error>>
+    where
+        T: Translator<Pk>,
+    {
+        Bare::new(self.ms.translate_pk(t)?).map_err(TranslateErr::OuterError)
+    }
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Bare<Pk> {
@@ -190,21 +198,6 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Bare<Pk> {
     }
 }
 
-impl<P, Q> TranslatePk<P, Q> for Bare<P>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    type Output = Bare<Q>;
-
-    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Bare<Q>, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>,
-    {
-        Bare::new(self.ms.translate_pk(t)?).map_err(TranslateErr::OuterError)
-    }
-}
-
 /// A bare PkH descriptor at top level
 #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
 pub struct Pkh<Pk: MiniscriptKey> {
@@ -260,6 +253,18 @@ impl<Pk: MiniscriptKey> Pkh<Pk> {
         note = "Use max_weight_to_satisfy instead. The method to count bytes was redesigned and the results will differ from max_weight_to_satisfy. For more details check rust-bitcoin/rust-miniscript#476."
     )]
     pub fn max_satisfaction_weight(&self) -> usize { 4 * (1 + 73 + BareCtx::pk_len(&self.pk)) }
+
+    /// Converts the keys in a script from one type to another.
+    pub fn translate_pk<T>(&self, t: &mut T) -> Result<Pkh<T::TargetPk>, TranslateErr<T::Error>>
+    where
+        T: Translator<Pk>,
+    {
+        let res = Pkh::new(t.pk(&self.pk)?);
+        match res {
+            Ok(pk) => Ok(pk),
+            Err(e) => Err(TranslateErr::OuterError(Error::from(e))),
+        }
+    }
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Pkh<Pk> {
@@ -391,22 +396,3 @@ impl<Pk: FromStrKey> core::str::FromStr for Pkh<Pk> {
 impl<Pk: MiniscriptKey> ForEachKey<Pk> for Pkh<Pk> {
     fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool { pred(&self.pk) }
 }
-
-impl<P, Q> TranslatePk<P, Q> for Pkh<P>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    type Output = Pkh<Q>;
-
-    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>,
-    {
-        let res = Pkh::new(t.pk(&self.pk)?);
-        match res {
-            Ok(pk) => Ok(pk),
-            Err(e) => Err(TranslateErr::OuterError(Error::from(e))),
-        }
-    }
-}
diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs
index 0ca62fc9f..7e034ef99 100644
--- a/src/descriptor/key.rs
+++ b/src/descriptor/key.rs
@@ -1003,9 +1003,7 @@ impl DefiniteDescriptorKey {
     /// always return a compressed key
     ///
     /// Will return an error if the descriptor key has any hardened derivation steps in its path. To
-    /// avoid this error you should replace any such public keys first with [`translate_pk`].
-    ///
-    /// [`translate_pk`]: crate::TranslatePk::translate_pk
+    /// avoid this error you should replace any such public keys first with [`crate::Descriptor::translate_pk`].
     pub fn derive_public_key<C: Verification>(
         &self,
         secp: &Secp256k1<C>,
diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs
index 0b0bd02b6..1a2a7da16 100644
--- a/src/descriptor/mod.rs
+++ b/src/descriptor/mod.rs
@@ -28,7 +28,7 @@ use crate::plan::{AssetProvider, Plan};
 use crate::prelude::*;
 use crate::{
     expression, hash256, BareCtx, Error, ForEachKey, FromStrKey, MiniscriptKey, Satisfier,
-    ToPublicKey, TranslateErr, TranslatePk, Translator,
+    ToPublicKey, TranslateErr, Translator,
 };
 
 mod bare;
@@ -359,6 +359,25 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
         };
         Ok(weight)
     }
+
+    /// Converts a descriptor using one kind of keys to another kind of key.
+    pub fn translate_pk<T>(
+        &self,
+        t: &mut T,
+    ) -> Result<Descriptor<T::TargetPk>, TranslateErr<T::Error>>
+    where
+        T: Translator<Pk>,
+    {
+        let desc = match *self {
+            Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(t)?),
+            Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(t)?),
+            Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(t)?),
+            Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(t)?),
+            Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(t)?),
+            Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(t)?),
+        };
+        Ok(desc)
+    }
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
@@ -551,30 +570,6 @@ impl Descriptor<DefiniteDescriptorKey> {
     }
 }
 
-impl<P, Q> TranslatePk<P, Q> for Descriptor<P>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    type Output = Descriptor<Q>;
-
-    /// Converts a descriptor using abstract keys to one using specific keys.
-    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>,
-    {
-        let desc = match *self {
-            Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(t)?),
-            Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(t)?),
-            Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(t)?),
-            Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(t)?),
-            Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(t)?),
-            Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(t)?),
-        };
-        Ok(desc)
-    }
-}
-
 impl<Pk: MiniscriptKey> ForEachKey<Pk> for Descriptor<Pk> {
     fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: F) -> bool {
         match *self {
@@ -607,7 +602,10 @@ impl Descriptor<DescriptorPublicKey> {
     ) -> Result<Descriptor<DefiniteDescriptorKey>, ConversionError> {
         struct Derivator(u32);
 
-        impl Translator<DescriptorPublicKey, DefiniteDescriptorKey, ConversionError> for Derivator {
+        impl Translator<DescriptorPublicKey> for Derivator {
+            type TargetPk = DefiniteDescriptorKey;
+            type Error = ConversionError;
+
             fn pk(
                 &mut self,
                 pk: &DescriptorPublicKey,
@@ -698,9 +696,10 @@ impl Descriptor<DescriptorPublicKey> {
 
         struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1<C>);
 
-        impl<'a, C: secp256k1::Signing> Translator<String, DescriptorPublicKey, Error>
-            for KeyMapWrapper<'a, C>
-        {
+        impl<'a, C: secp256k1::Signing> Translator<String> for KeyMapWrapper<'a, C> {
+            type TargetPk = DescriptorPublicKey;
+            type Error = Error;
+
             fn pk(&mut self, pk: &String) -> Result<DescriptorPublicKey, Error> {
                 parse_key(pk, &mut self.0, self.1)
             }
@@ -745,29 +744,35 @@ impl Descriptor<DescriptorPublicKey> {
     pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String {
         struct KeyMapLookUp<'a>(&'a KeyMap);
 
-        impl<'a> Translator<DescriptorPublicKey, String, ()> for KeyMapLookUp<'a> {
-            fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, ()> {
+        impl<'a> Translator<DescriptorPublicKey> for KeyMapLookUp<'a> {
+            type TargetPk = String;
+            type Error = core::convert::Infallible;
+
+            fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, Self::Error> {
                 key_to_string(pk, self.0)
             }
 
-            fn sha256(&mut self, sha256: &sha256::Hash) -> Result<String, ()> {
+            fn sha256(&mut self, sha256: &sha256::Hash) -> Result<String, Self::Error> {
                 Ok(sha256.to_string())
             }
 
-            fn hash256(&mut self, hash256: &hash256::Hash) -> Result<String, ()> {
+            fn hash256(&mut self, hash256: &hash256::Hash) -> Result<String, Self::Error> {
                 Ok(hash256.to_string())
             }
 
-            fn ripemd160(&mut self, ripemd160: &ripemd160::Hash) -> Result<String, ()> {
+            fn ripemd160(&mut self, ripemd160: &ripemd160::Hash) -> Result<String, Self::Error> {
                 Ok(ripemd160.to_string())
             }
 
-            fn hash160(&mut self, hash160: &hash160::Hash) -> Result<String, ()> {
+            fn hash160(&mut self, hash160: &hash160::Hash) -> Result<String, Self::Error> {
                 Ok(hash160.to_string())
             }
         }
 
-        fn key_to_string(pk: &DescriptorPublicKey, key_map: &KeyMap) -> Result<String, ()> {
+        fn key_to_string(
+            pk: &DescriptorPublicKey,
+            key_map: &KeyMap,
+        ) -> Result<String, core::convert::Infallible> {
             Ok(match key_map.get(pk) {
                 Some(secret) => secret.to_string(),
                 None => pk.to_string(),
@@ -842,7 +847,10 @@ impl Descriptor<DescriptorPublicKey> {
 
         // Now, transform the multipath key of each descriptor into a single-key using each index.
         struct IndexChoser(usize);
-        impl Translator<DescriptorPublicKey, DescriptorPublicKey, Error> for IndexChoser {
+        impl Translator<DescriptorPublicKey> for IndexChoser {
+            type TargetPk = DescriptorPublicKey;
+            type Error = Error;
+
             fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<DescriptorPublicKey, Error> {
                 match pk {
                     DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => {
@@ -899,10 +907,10 @@ impl Descriptor<DefiniteDescriptorKey> {
     ) -> Result<Descriptor<bitcoin::PublicKey>, ConversionError> {
         struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1<C>);
 
-        impl<'a, C: secp256k1::Verification>
-            Translator<DefiniteDescriptorKey, bitcoin::PublicKey, ConversionError>
-            for Derivator<'a, C>
-        {
+        impl<'a, C: secp256k1::Verification> Translator<DefiniteDescriptorKey> for Derivator<'a, C> {
+            type TargetPk = bitcoin::PublicKey;
+            type Error = ConversionError;
+
             fn pk(
                 &mut self,
                 pk: &DefiniteDescriptorKey,
diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs
index b04c1ad3a..2f2532b85 100644
--- a/src/descriptor/segwitv0.rs
+++ b/src/descriptor/segwitv0.rs
@@ -22,7 +22,7 @@ use crate::prelude::*;
 use crate::util::varint_len;
 use crate::{
     Error, ForEachKey, FromStrKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey,
-    TranslateErr, TranslatePk, Translator,
+    TranslateErr, Translator,
 };
 /// A Segwitv0 wsh descriptor
 #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
@@ -128,6 +128,18 @@ impl<Pk: MiniscriptKey> Wsh<Pk> {
             varint_len(max_sat_elems) +
             max_sat_size)
     }
+
+    /// Converts the keys in a script from one type to another.
+    pub fn translate_pk<T>(&self, t: &mut T) -> Result<Wsh<T::TargetPk>, TranslateErr<T::Error>>
+    where
+        T: Translator<Pk>,
+    {
+        let inner = match self.inner {
+            WshInner::SortedMulti(ref smv) => WshInner::SortedMulti(smv.translate_pk(t)?),
+            WshInner::Ms(ref ms) => WshInner::Ms(ms.translate_pk(t)?),
+        };
+        Ok(Wsh { inner })
+    }
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Wsh<Pk> {
@@ -291,25 +303,6 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Wsh<Pk> {
     }
 }
 
-impl<P, Q> TranslatePk<P, Q> for Wsh<P>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    type Output = Wsh<Q>;
-
-    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>,
-    {
-        let inner = match self.inner {
-            WshInner::SortedMulti(ref smv) => WshInner::SortedMulti(smv.translate_pk(t)?),
-            WshInner::Ms(ref ms) => WshInner::Ms(ms.translate_pk(t)?),
-        };
-        Ok(Wsh { inner })
-    }
-}
-
 /// A bare Wpkh descriptor at top level
 #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
 pub struct Wpkh<Pk: MiniscriptKey> {
@@ -370,6 +363,18 @@ impl<Pk: MiniscriptKey> Wpkh<Pk> {
         note = "Use max_weight_to_satisfy instead. The method to count bytes was redesigned and the results will differ from max_weight_to_satisfy. For more details check rust-bitcoin/rust-miniscript#476."
     )]
     pub fn max_satisfaction_weight(&self) -> usize { 4 + 1 + 73 + Segwitv0::pk_len(&self.pk) }
+
+    /// Converts the keys in a script from one type to another.
+    pub fn translate_pk<T>(&self, t: &mut T) -> Result<Wpkh<T::TargetPk>, TranslateErr<T::Error>>
+    where
+        T: Translator<Pk>,
+    {
+        let res = Wpkh::new(t.pk(&self.pk)?);
+        match res {
+            Ok(pk) => Ok(pk),
+            Err(e) => Err(TranslateErr::OuterError(Error::from(e))),
+        }
+    }
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Wpkh<Pk> {
@@ -509,22 +514,3 @@ impl<Pk: FromStrKey> core::str::FromStr for Wpkh<Pk> {
 impl<Pk: MiniscriptKey> ForEachKey<Pk> for Wpkh<Pk> {
     fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool { pred(&self.pk) }
 }
-
-impl<P, Q> TranslatePk<P, Q> for Wpkh<P>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    type Output = Wpkh<Q>;
-
-    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>,
-    {
-        let res = Wpkh::new(t.pk(&self.pk)?);
-        match res {
-            Ok(pk) => Ok(pk),
-            Err(e) => Err(TranslateErr::OuterError(Error::from(e))),
-        }
-    }
-}
diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs
index c575c3476..cf05c1b71 100644
--- a/src/descriptor/sh.rs
+++ b/src/descriptor/sh.rs
@@ -25,7 +25,7 @@ use crate::prelude::*;
 use crate::util::{varint_len, witness_to_scriptsig};
 use crate::{
     push_opcode_size, Error, ForEachKey, FromStrKey, Legacy, Miniscript, MiniscriptKey, Satisfier,
-    Segwitv0, ToPublicKey, TranslateErr, TranslatePk, Translator,
+    Segwitv0, ToPublicKey, TranslateErr, Translator,
 };
 
 /// A Legacy p2sh Descriptor
@@ -259,6 +259,20 @@ impl<Pk: MiniscriptKey> Sh<Pk> {
             }
         })
     }
+
+    /// Converts the keys in a script from one type to another.
+    pub fn translate_pk<T>(&self, t: &mut T) -> Result<Sh<T::TargetPk>, TranslateErr<T::Error>>
+    where
+        T: Translator<Pk>,
+    {
+        let inner = match self.inner {
+            ShInner::Wsh(ref wsh) => ShInner::Wsh(wsh.translate_pk(t)?),
+            ShInner::Wpkh(ref wpkh) => ShInner::Wpkh(wpkh.translate_pk(t)?),
+            ShInner::SortedMulti(ref smv) => ShInner::SortedMulti(smv.translate_pk(t)?),
+            ShInner::Ms(ref ms) => ShInner::Ms(ms.translate_pk(t)?),
+        };
+        Ok(Sh { inner })
+    }
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Sh<Pk> {
@@ -444,24 +458,3 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Sh<Pk> {
         }
     }
 }
-
-impl<P, Q> TranslatePk<P, Q> for Sh<P>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    type Output = Sh<Q>;
-
-    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>,
-    {
-        let inner = match self.inner {
-            ShInner::Wsh(ref wsh) => ShInner::Wsh(wsh.translate_pk(t)?),
-            ShInner::Wpkh(ref wpkh) => ShInner::Wpkh(wpkh.translate_pk(t)?),
-            ShInner::SortedMulti(ref smv) => ShInner::SortedMulti(smv.translate_pk(t)?),
-            ShInner::Ms(ref ms) => ShInner::Ms(ms.translate_pk(t)?),
-        };
-        Ok(Sh { inner })
-    }
-}
diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs
index 5b7079292..b8a378a26 100644
--- a/src/descriptor/sortedmulti.rs
+++ b/src/descriptor/sortedmulti.rs
@@ -77,13 +77,12 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> SortedMultiVec<Pk, Ctx> {
     /// This will panic if fpk returns an uncompressed key when
     /// converting to a Segwit descriptor. To prevent this panic, ensure
     /// fpk returns an error in this case instead.
-    pub fn translate_pk<T, Q, FuncError>(
+    pub fn translate_pk<T>(
         &self,
         t: &mut T,
-    ) -> Result<SortedMultiVec<Q, Ctx>, TranslateErr<FuncError>>
+    ) -> Result<SortedMultiVec<T::TargetPk, Ctx>, TranslateErr<T::Error>>
     where
-        T: Translator<Pk, Q, FuncError>,
-        Q: MiniscriptKey,
+        T: Translator<Pk>,
     {
         let ret = SortedMultiVec {
             inner: self.inner.translate_ref(|pk| t.pk(pk))?,
diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs
index 6018551c9..2f505780c 100644
--- a/src/descriptor/tr.rs
+++ b/src/descriptor/tr.rs
@@ -24,7 +24,7 @@ use crate::prelude::*;
 use crate::util::{varint_len, witness_size};
 use crate::{
     errstr, Error, ForEachKey, FromStrKey, MiniscriptKey, Satisfier, ScriptContext, Tap, Threshold,
-    ToPublicKey, TranslateErr, TranslatePk, Translator,
+    ToPublicKey, TranslateErr, Translator,
 };
 
 /// A Taproot Tree representation.
@@ -132,10 +132,9 @@ impl<Pk: MiniscriptKey> TapTree<Pk> {
     pub fn iter(&self) -> TapTreeIter<Pk> { TapTreeIter { stack: vec![(0, self)] } }
 
     // Helper function to translate keys
-    fn translate_helper<T, Q, E>(&self, t: &mut T) -> Result<TapTree<Q>, TranslateErr<E>>
+    fn translate_helper<T>(&self, t: &mut T) -> Result<TapTree<T::TargetPk>, TranslateErr<T::Error>>
     where
-        T: Translator<Pk, Q, E>,
-        Q: MiniscriptKey,
+        T: Translator<Pk>,
     {
         let frag = match *self {
             TapTree::Tree { ref left, ref right, ref height } => TapTree::Tree {
@@ -351,6 +350,23 @@ impl<Pk: MiniscriptKey> Tr<Pk> {
             .max()
             .ok_or(Error::ImpossibleSatisfaction)
     }
+
+    /// Converts keys from one type of public key to another.
+    pub fn translate_pk<T>(
+        &self,
+        translate: &mut T,
+    ) -> Result<Tr<T::TargetPk>, TranslateErr<T::Error>>
+    where
+        T: Translator<Pk>,
+    {
+        let tree = match &self.tree {
+            Some(tree) => Some(tree.translate_helper(translate)?),
+            None => None,
+        };
+        let translate_desc =
+            Tr::new(translate.pk(&self.internal_key)?, tree).map_err(TranslateErr::OuterError)?;
+        Ok(translate_desc)
+    }
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Tr<Pk> {
@@ -652,27 +668,6 @@ impl<Pk: MiniscriptKey> ForEachKey<Pk> for Tr<Pk> {
     }
 }
 
-impl<P, Q> TranslatePk<P, Q> for Tr<P>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    type Output = Tr<Q>;
-
-    fn translate_pk<T, E>(&self, translate: &mut T) -> Result<Self::Output, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>,
-    {
-        let tree = match &self.tree {
-            Some(tree) => Some(tree.translate_helper(translate)?),
-            None => None,
-        };
-        let translate_desc = Tr::new(translate.pk(&self.internal_key)?, tree)
-            .map_err(|e| TranslateErr::OuterError(e))?;
-        Ok(translate_desc)
-    }
-}
-
 // Helper function to compute the len of control block at a given depth
 fn control_block_len(depth: u8) -> usize {
     TAPROOT_CONTROL_BASE_SIZE + (depth as usize) * TAPROOT_CONTROL_NODE_SIZE
diff --git a/src/interpreter/inner.rs b/src/interpreter/inner.rs
index e607266e7..49134c6de 100644
--- a/src/interpreter/inner.rs
+++ b/src/interpreter/inner.rs
@@ -356,12 +356,15 @@ impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::PublicKey, Ctx> {
     fn to_no_checks_ms(&self) -> Miniscript<BitcoinKey, NoChecks> {
         struct TranslateFullPk;
 
-        impl Translator<bitcoin::PublicKey, BitcoinKey, ()> for TranslateFullPk {
-            fn pk(&mut self, pk: &bitcoin::PublicKey) -> Result<BitcoinKey, ()> {
+        impl Translator<bitcoin::PublicKey> for TranslateFullPk {
+            type TargetPk = BitcoinKey;
+            type Error = core::convert::Infallible;
+
+            fn pk(&mut self, pk: &bitcoin::PublicKey) -> Result<BitcoinKey, Self::Error> {
                 Ok(BitcoinKey::Fullkey(*pk))
             }
 
-            translate_hash_clone!(bitcoin::PublicKey, BitcoinKey, ());
+            translate_hash_clone!(bitcoin::PublicKey, BitcoinKey, Self::Error);
         }
 
         self.translate_pk_ctx(&mut TranslateFullPk)
@@ -371,15 +374,17 @@ impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::PublicKey, Ctx> {
 
 impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::key::XOnlyPublicKey, Ctx> {
     fn to_no_checks_ms(&self) -> Miniscript<BitcoinKey, NoChecks> {
-        // specify the () error type as this cannot error
         struct TranslateXOnlyPk;
 
-        impl Translator<bitcoin::key::XOnlyPublicKey, BitcoinKey, ()> for TranslateXOnlyPk {
-            fn pk(&mut self, pk: &bitcoin::key::XOnlyPublicKey) -> Result<BitcoinKey, ()> {
+        impl Translator<bitcoin::key::XOnlyPublicKey> for TranslateXOnlyPk {
+            type TargetPk = BitcoinKey;
+            type Error = core::convert::Infallible;
+
+            fn pk(&mut self, pk: &bitcoin::key::XOnlyPublicKey) -> Result<BitcoinKey, Self::Error> {
                 Ok(BitcoinKey::XOnlyPublicKey(*pk))
             }
 
-            translate_hash_clone!(bitcoin::key::XOnlyPublicKey, BitcoinKey, ());
+            translate_hash_clone!(bitcoin::key::XOnlyPublicKey, BitcoinKey, Self::Error);
         }
         self.translate_pk_ctx(&mut TranslateXOnlyPk)
             .expect("Translation should succeed")
diff --git a/src/lib.rs b/src/lib.rs
index b463807ce..4cf566392 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -296,25 +296,38 @@ impl ToPublicKey for bitcoin::secp256k1::XOnlyPublicKey {
 
 /// Describes an object that can translate various keys and hashes from one key to the type
 /// associated with the other key. Used by the [`TranslatePk`] trait to do the actual translations.
-pub trait Translator<P, Q, E>
-where
-    P: MiniscriptKey,
-    Q: MiniscriptKey,
-{
-    /// Translates public keys P -> Q.
-    fn pk(&mut self, pk: &P) -> Result<Q, E>;
-
-    /// Provides the translation from P::Sha256 -> Q::Sha256
-    fn sha256(&mut self, sha256: &P::Sha256) -> Result<Q::Sha256, E>;
-
-    /// Provides the translation from P::Hash256 -> Q::Hash256
-    fn hash256(&mut self, hash256: &P::Hash256) -> Result<Q::Hash256, E>;
-
-    /// Translates ripemd160 hashes from P::Ripemd160 -> Q::Ripemd160
-    fn ripemd160(&mut self, ripemd160: &P::Ripemd160) -> Result<Q::Ripemd160, E>;
-
-    /// Translates hash160 hashes from P::Hash160 -> Q::Hash160
-    fn hash160(&mut self, hash160: &P::Hash160) -> Result<Q::Hash160, E>;
+pub trait Translator<P: MiniscriptKey> {
+    /// The public key (and associated hash types that this translator converts to.
+    type TargetPk: MiniscriptKey;
+    /// An error that may occur during transalation.
+    type Error;
+
+    /// Translates keys.
+    fn pk(&mut self, pk: &P) -> Result<Self::TargetPk, Self::Error>;
+
+    /// Translates SHA256 hashes.
+    fn sha256(
+        &mut self,
+        sha256: &P::Sha256,
+    ) -> Result<<Self::TargetPk as MiniscriptKey>::Sha256, Self::Error>;
+
+    /// Translates HASH256 hashes.
+    fn hash256(
+        &mut self,
+        hash256: &P::Hash256,
+    ) -> Result<<Self::TargetPk as MiniscriptKey>::Hash256, Self::Error>;
+
+    /// Translates RIPEMD160 hashes.
+    fn ripemd160(
+        &mut self,
+        ripemd160: &P::Ripemd160,
+    ) -> Result<<Self::TargetPk as MiniscriptKey>::Ripemd160, Self::Error>;
+
+    /// Translates HASH160 hashes.
+    fn hash160(
+        &mut self,
+        hash160: &P::Hash160,
+    ) -> Result<<Self::TargetPk as MiniscriptKey>::Hash160, Self::Error>;
 }
 
 /// An enum for representing translation errors
@@ -368,31 +381,14 @@ impl<E: fmt::Debug> fmt::Debug for TranslateErr<E> {
 
 /// Converts a descriptor using abstract keys to one using specific keys. Uses translator `t` to do
 /// the actual translation function calls.
+#[deprecated(since = "TBD", note = "This trait no longer needs to be imported.")]
 pub trait TranslatePk<P, Q>
 where
     P: MiniscriptKey,
     Q: MiniscriptKey,
 {
-    /// The associated output type. This must be `Self<Q>`.
-    type Output;
-
-    /// Translates a struct from one generic to another where the translations
-    /// for Pk are provided by the given [`Translator`].
-    fn translate_pk<T, E>(&self, translator: &mut T) -> Result<Self::Output, TranslateErr<E>>
-    where
-        T: Translator<P, Q, E>;
 }
 
-/// Either a key or keyhash, but both contain Pk
-// pub struct ForEach<'a, Pk: MiniscriptKey>(&'a Pk);
-
-// impl<'a, Pk: MiniscriptKey<Hash = Pk>> ForEach<'a, Pk> {
-//     /// Convenience method to avoid distinguishing between keys and hashes when these are the same type
-//     pub fn as_key(&self) -> &'a Pk {
-//         self.0
-//     }
-// }
-
 /// Trait describing the ability to iterate over every key
 pub trait ForEachKey<Pk: MiniscriptKey> {
     /// Run a predicate on every key in the descriptor, returning whether
diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs
index 5a48d74a2..1a68606ba 100644
--- a/src/miniscript/mod.rs
+++ b/src/miniscript/mod.rs
@@ -43,8 +43,7 @@ use self::lex::{lex, TokenIter};
 pub use crate::miniscript::context::ScriptContext;
 use crate::miniscript::decode::Terminal;
 use crate::{
-    expression, plan, Error, ForEachKey, FromStrKey, MiniscriptKey, ToPublicKey, TranslatePk,
-    Translator,
+    expression, plan, Error, ForEachKey, FromStrKey, MiniscriptKey, ToPublicKey, Translator,
 };
 #[cfg(test)]
 mod ms_tests;
@@ -514,33 +513,26 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> ForEachKey<Pk> for Miniscript<Pk, Ct
     }
 }
 
-impl<Pk, Q, Ctx> TranslatePk<Pk, Q> for Miniscript<Pk, Ctx>
-where
-    Pk: MiniscriptKey,
-    Q: MiniscriptKey,
-    Ctx: ScriptContext,
-{
-    type Output = Miniscript<Q, Ctx>;
-
+impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     /// Translates a struct from one generic to another where the translation
     /// for Pk is provided by [`Translator`]
-    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, TranslateErr<E>>
+    pub fn translate_pk<T>(
+        &self,
+        t: &mut T,
+    ) -> Result<Miniscript<T::TargetPk, Ctx>, TranslateErr<T::Error>>
     where
-        T: Translator<Pk, Q, E>,
+        T: Translator<Pk>,
     {
         self.translate_pk_ctx(t)
     }
-}
 
-impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
-    pub(super) fn translate_pk_ctx<Q, CtxQ, T, FuncError>(
+    pub(super) fn translate_pk_ctx<CtxQ, T>(
         &self,
         t: &mut T,
-    ) -> Result<Miniscript<Q, CtxQ>, TranslateErr<FuncError>>
+    ) -> Result<Miniscript<T::TargetPk, CtxQ>, TranslateErr<T::Error>>
     where
-        Q: MiniscriptKey,
         CtxQ: ScriptContext,
-        T: Translator<Pk, Q, FuncError>,
+        T: Translator<Pk>,
     {
         let mut translated = vec![];
         for data in self.rtl_post_order_iter() {
@@ -837,7 +829,7 @@ mod tests {
     use crate::policy::Liftable;
     use crate::prelude::*;
     use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator};
-    use crate::{hex_script, Error, ExtParams, RelLockTime, Satisfier, ToPublicKey, TranslatePk};
+    use crate::{hex_script, Error, ExtParams, RelLockTime, Satisfier, ToPublicKey};
 
     type Segwitv0Script = Miniscript<bitcoin::PublicKey, Segwitv0>;
     type Tapscript = Miniscript<bitcoin::secp256k1::XOnlyPublicKey, Tap>;
diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs
index 6382d837b..8e31d5474 100644
--- a/src/policy/concrete.rs
+++ b/src/policy/concrete.rs
@@ -507,10 +507,9 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     /// Converts a policy using one kind of public key to another type of public key.
     ///
     /// For example usage please see [`crate::policy::semantic::Policy::translate_pk`].
-    pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E>
+    pub fn translate_pk<T>(&self, t: &mut T) -> Result<Policy<T::TargetPk>, T::Error>
     where
-        T: Translator<Pk, Q, E>,
-        Q: MiniscriptKey,
+        T: Translator<Pk>,
     {
         use Policy::*;
 
@@ -1140,15 +1139,26 @@ mod tests {
     #[test]
     fn tranaslate_pk() {
         pub struct TestTranslator;
-        impl Translator<String, String, ()> for TestTranslator {
-            fn pk(&mut self, pk: &String) -> Result<String, ()> {
+        impl Translator<String> for TestTranslator {
+            type TargetPk = String;
+            type Error = core::convert::Infallible;
+
+            fn pk(&mut self, pk: &String) -> Result<String, Self::Error> {
                 let new = format!("NEW-{}", pk);
                 Ok(new.to_string())
             }
-            fn sha256(&mut self, hash: &String) -> Result<String, ()> { Ok(hash.to_string()) }
-            fn hash256(&mut self, hash: &String) -> Result<String, ()> { Ok(hash.to_string()) }
-            fn ripemd160(&mut self, hash: &String) -> Result<String, ()> { Ok(hash.to_string()) }
-            fn hash160(&mut self, hash: &String) -> Result<String, ()> { Ok(hash.to_string()) }
+            fn sha256(&mut self, hash: &String) -> Result<String, Self::Error> {
+                Ok(hash.to_string())
+            }
+            fn hash256(&mut self, hash: &String) -> Result<String, Self::Error> {
+                Ok(hash.to_string())
+            }
+            fn ripemd160(&mut self, hash: &String) -> Result<String, Self::Error> {
+                Ok(hash.to_string())
+            }
+            fn hash160(&mut self, hash: &String) -> Result<String, Self::Error> {
+                Ok(hash.to_string())
+            }
         }
         let policy = Policy::<String>::from_str("or(and(pk(A),pk(B)),pk(C))").unwrap();
         let mut t = TestTranslator;
diff --git a/src/policy/semantic.rs b/src/policy/semantic.rs
index 7d2959764..62487a54a 100644
--- a/src/policy/semantic.rs
+++ b/src/policy/semantic.rs
@@ -120,8 +120,11 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     ///
     /// // If we also wanted to provide mapping of other associated types (sha256, older etc),
     /// // we would use the general [`Translator`] trait.
-    /// impl Translator<String, bitcoin::PublicKey, ()> for StrPkTranslator {
-    ///     fn pk(&mut self, pk: &String) -> Result<bitcoin::PublicKey, ()> {
+    /// impl Translator<String> for StrPkTranslator {
+    ///     type TargetPk = bitcoin::PublicKey;
+    ///     type Error = ();
+    ///
+    ///     fn pk(&mut self, pk: &String) -> Result<bitcoin::PublicKey, Self::Error> {
     ///         self.pk_map.get(pk).copied().ok_or(()) // Dummy Err
     ///     }
     ///
@@ -140,10 +143,9 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
     /// let expected_policy = Policy::from_str(&format!("and(pk({}),pk({}))", alice_pk, bob_pk)).unwrap();
     /// assert_eq!(real_policy, expected_policy);
     /// ```
-    pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E>
+    pub fn translate_pk<T>(&self, t: &mut T) -> Result<Policy<T::TargetPk>, T::Error>
     where
-        T: Translator<Pk, Q, E>,
-        Q: MiniscriptKey,
+        T: Translator<Pk>,
     {
         use Policy::*;
 
diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs
index 2c489ca89..96615df4c 100644
--- a/src/psbt/mod.rs
+++ b/src/psbt/mod.rs
@@ -25,7 +25,7 @@ use crate::miniscript::context::SigType;
 use crate::prelude::*;
 use crate::{
     descriptor, interpreter, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, MiniscriptKey,
-    Preimage32, Satisfier, ToPublicKey, TranslatePk, Translator,
+    Preimage32, Satisfier, ToPublicKey, Translator,
 };
 
 mod finalizer;
@@ -976,9 +976,10 @@ struct KeySourceLookUp(
     pub secp256k1::Secp256k1<VerifyOnly>,
 );
 
-impl Translator<DefiniteDescriptorKey, bitcoin::PublicKey, descriptor::ConversionError>
-    for KeySourceLookUp
-{
+impl Translator<DefiniteDescriptorKey> for KeySourceLookUp {
+    type TargetPk = bitcoin::PublicKey;
+    type Error = descriptor::ConversionError;
+
     fn pk(
         &mut self,
         xpk: &DefiniteDescriptorKey,
diff --git a/src/pub_macros.rs b/src/pub_macros.rs
index bdb0d59b4..538d37f3c 100644
--- a/src/pub_macros.rs
+++ b/src/pub_macros.rs
@@ -27,9 +27,12 @@
 ///
 /// // If we also wanted to provide mapping of other associated types(sha256, older etc),
 /// // we would use the general Translator Trait.
-/// impl Translator<String, bitcoin::PublicKey, ()> for StrPkTranslator {
+/// impl Translator<String> for StrPkTranslator {
+///     type TargetPk = bitcoin::PublicKey;
+///     type Error = ();
+///
 ///     // Provides the translation public keys P -> Q
-///     fn pk(&mut self, pk: &String) -> Result<bitcoin::PublicKey, ()> {
+///     fn pk(&mut self, pk: &String) -> Result<bitcoin::PublicKey, Self::Error> {
 ///         self.pk_map.get(pk).copied().ok_or(()) // Dummy Err
 ///     }
 ///
diff --git a/src/test_utils.rs b/src/test_utils.rs
index 170e3c7f6..9d52b7c25 100644
--- a/src/test_utils.rs
+++ b/src/test_utils.rs
@@ -2,6 +2,7 @@
 
 //! Generally useful utilities for test scripts
 
+use core::convert::Infallible;
 use std::collections::HashMap;
 use std::str::FromStr;
 
@@ -25,8 +26,11 @@ pub struct StrKeyTranslator {
     pub hash160_map: HashMap<String, hash160::Hash>,
 }
 
-impl Translator<String, bitcoin::PublicKey, ()> for StrKeyTranslator {
-    fn pk(&mut self, pk: &String) -> Result<bitcoin::PublicKey, ()> {
+impl Translator<String> for StrKeyTranslator {
+    type TargetPk = bitcoin::PublicKey;
+    type Error = Infallible;
+
+    fn pk(&mut self, pk: &String) -> Result<bitcoin::PublicKey, Infallible> {
         let key = self.pk_map.get(pk).copied().unwrap_or_else(|| {
             bitcoin::PublicKey::from_str(
                 "02c2122e30e73f7fe37986e3f81ded00158e94b7ad472369b83bbdd28a9a198a39",
@@ -36,7 +40,7 @@ impl Translator<String, bitcoin::PublicKey, ()> for StrKeyTranslator {
         Ok(key)
     }
 
-    fn sha256(&mut self, _sha256: &String) -> Result<sha256::Hash, ()> {
+    fn sha256(&mut self, _sha256: &String) -> Result<sha256::Hash, Infallible> {
         let hash = sha256::Hash::from_str(
             "4ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260",
         )
@@ -44,7 +48,7 @@ impl Translator<String, bitcoin::PublicKey, ()> for StrKeyTranslator {
         Ok(hash)
     }
 
-    fn hash256(&mut self, _hash256: &String) -> Result<hash256::Hash, ()> {
+    fn hash256(&mut self, _hash256: &String) -> Result<hash256::Hash, Infallible> {
         // hard coded value
         let hash = hash256::Hash::from_str(
             "4ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260",
@@ -53,12 +57,12 @@ impl Translator<String, bitcoin::PublicKey, ()> for StrKeyTranslator {
         Ok(hash)
     }
 
-    fn ripemd160(&mut self, _ripemd160: &String) -> Result<ripemd160::Hash, ()> {
+    fn ripemd160(&mut self, _ripemd160: &String) -> Result<ripemd160::Hash, Infallible> {
         let hash = ripemd160::Hash::from_str("4ae81572f06e1b88fd5ced7a1a00094543a0069").unwrap();
         Ok(hash)
     }
 
-    fn hash160(&mut self, _hash160: &String) -> Result<hash160::Hash, ()> {
+    fn hash160(&mut self, _hash160: &String) -> Result<hash160::Hash, Infallible> {
         let hash = hash160::Hash::from_str("4ae81572f06e1b88fd5ced7a1a00094543a0069").unwrap();
         Ok(hash)
     }
@@ -74,8 +78,11 @@ pub struct StrXOnlyKeyTranslator {
     pub hash160_map: HashMap<String, hash160::Hash>,
 }
 
-impl Translator<String, XOnlyPublicKey, ()> for StrXOnlyKeyTranslator {
-    fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, ()> {
+impl Translator<String> for StrXOnlyKeyTranslator {
+    type TargetPk = XOnlyPublicKey;
+    type Error = Infallible;
+
+    fn pk(&mut self, pk: &String) -> Result<XOnlyPublicKey, Infallible> {
         let key = self.pk_map.get(pk).copied().unwrap_or_else(|| {
             XOnlyPublicKey::from_str(
                 "c2122e30e73f7fe37986e3f81ded00158e94b7ad472369b83bbdd28a9a198a39",
@@ -85,7 +92,7 @@ impl Translator<String, XOnlyPublicKey, ()> for StrXOnlyKeyTranslator {
         Ok(key)
     }
 
-    fn sha256(&mut self, _sha256: &String) -> Result<sha256::Hash, ()> {
+    fn sha256(&mut self, _sha256: &String) -> Result<sha256::Hash, Infallible> {
         let hash = sha256::Hash::from_str(
             "4ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260",
         )
@@ -93,7 +100,7 @@ impl Translator<String, XOnlyPublicKey, ()> for StrXOnlyKeyTranslator {
         Ok(hash)
     }
 
-    fn hash256(&mut self, _hash256: &String) -> Result<hash256::Hash, ()> {
+    fn hash256(&mut self, _hash256: &String) -> Result<hash256::Hash, Infallible> {
         let hash = hash256::Hash::from_str(
             "4ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260",
         )
@@ -101,12 +108,12 @@ impl Translator<String, XOnlyPublicKey, ()> for StrXOnlyKeyTranslator {
         Ok(hash)
     }
 
-    fn ripemd160(&mut self, _ripemd160: &String) -> Result<ripemd160::Hash, ()> {
+    fn ripemd160(&mut self, _ripemd160: &String) -> Result<ripemd160::Hash, Infallible> {
         let hash = ripemd160::Hash::from_str("4ae81572f06e1b88fd5ced7a1a00094543a0069").unwrap();
         Ok(hash)
     }
 
-    fn hash160(&mut self, _hash160: &String) -> Result<hash160::Hash, ()> {
+    fn hash160(&mut self, _hash160: &String) -> Result<hash160::Hash, Infallible> {
         let hash = hash160::Hash::from_str("4ae81572f06e1b88fd5ced7a1a00094543a0069").unwrap();
         Ok(hash)
     }