是否可以推断类型参数的类型?

时间:2019-02-04 17:43:54

标签: typescript generics

所以,在我的打字稿中有以下枚举:

import { Client, Configuration } from "bugsnag-react-native";
class CrashReporter {
 attach() {
const configuration = new Configuration();

  configuration.appVersion = "1.4.5";
  configuration.apiKey = "0096f**acb8**c76e**ecf6e**a2";
this.bugsnag = new Client(configuration);

 }

  setCurrentUser(userName, userID, userEmail) {
  this.bugsnag.setUser(userID, userName, userEmail);
  }

  notify(error) {
  this.bugsnag.notify(new Error(error));
  }
 }

 export default new CrashReporter();

我在如下所示的通用类中使用了它们:

enum TOKEN { OMG = 'OMG', ZRX = 'ZRX', ... }
enum CONTRACT_ADDRESS { OMG = '0x123...', ZRX = '0x3333.....' ... }
enum PRECISIONS = { OMG = 18, ZRX = 5, ... }

然后,出于命名目的,我添加:

class ERC20Tx<T extends TOKEN, P extends PRECISIONS, A extends CONTRACT_ADDRESS>{ ... }

最后,我有一堂课,其任务是将ETHTx转换为上述ERC20之一。

class OMG extends ERC20Tx<TOKEN.OMG, PRECISIONS.OMG, CONTRACT_ADDRESS.OMG> {}
class ZRX extends ERC20Tx<TOKEN.ZRX, PRECISIONS.ZRX, CONTRACT_ADDRESS.ZRX> {}

最后,我的问题是,是否可以简化ETH2ERC20的类型,因为我更喜欢这样称呼他们:

 class ETH2ERC20<E extends ERC20<T, P, A>, T extends TOKEN, P extends PRECISIONS, A extends CONTRACT_ADDRESS> { ... }

代替:

 const omgStream = new ETH2ERC20<OMG>({ ... });
 const zrxStream = new ETH2ERC20<ZRX>({ ... });

有可能吗?因为OMG / ZRX / ...已经包含了信息T,P,A?

2 个答案:

答案 0 :(得分:2)

只要您的类structurally依赖于TPA等类型,这当然是可能的(例如,这些类可以具有类型TPA的属性。)如果不这样做,编译器将be unable to infer the type parameters from your classes

无论如何,让我们看看您的类型。我建议的第一个简化只是使用与您拥有的三个K对象的键相对应的单个类型参数enum。由于这些键都是相同的键,而且看起来也不像是混合匹配类型(例如,您不会使用class OMX extends ERC20Tx<TOKEN.OMG, PRECISIONS.OMG, CONTRACT_ADDRESS.ZRX> {}),因此额外的类型参数是多余的:

class ERC20TxSimpler<K extends keyof typeof TOKEN>{
  k!: K;
  t!: (typeof TOKEN)[K];
  p!: (typeof PRECISIONS)[K];
  a!: (typeof CONTRACT_ADDRESS)[K];
}
class OMGSimpler extends ERC20TxSimpler<'OMG'> { }
class ZRXSimpler extends ERC20TxSimpler<'ZRX'> { }

请注意,我已经在t中创建了与您的旧p,{{相对应的类型}的属性aERC20TxSimpler<K>T 1}}和P类型参数。我还制作了一个A类型的k属性。只要您对这些类型还有其他牢固的结构依赖性,就不必严格要求这样做,但是我将在以后使用这些属性。

第二个简化是利用编译器的能力来推断和(或)look up类型,以允许您省去K类中的冗余类型参数:

ETH2ERC20

请注意,此类也具有属性,显示了如何找出与旧类型参数相对应的类型。例如,class ETH2ERC20Simpler<E extends ERC20TxSimpler<keyof typeof TOKEN>> { e!: E; t!: E['t']; p!: E['p']; a!: E['a']; } 的类型为ETH2ERC20Simpler<E>.p,这意味着它将在您以E['p']传递的任何内容中查找p属性。例如:

E

查找属性并不是将类型拉出其他类型的唯一方法。通常,只要结构上的依赖关系足够简单,就可以使用conditional type inference提取以下类型:

declare const eth2erc20omg: ETH2ERC20Simpler<OMGSimpler>;
eth2erc20omg.e; //OMGSimpler
eth2erc20omg.a; //CONTACT_ADDRESS.OMG
eth2erc20omg.p; //PRECISIONS.OMG
eth2erc20omg.t; //TOKEN.OMG

这与前面的代码具有相同的效果,但是它依赖于条件类型中的type KfromE<E> = [E] extends [ERC20TxSimpler<infer K>] ? K : never; class ETH2ERC20Simpler<E extends ERC20TxSimpler<keyof typeof TOKEN>> { e!: E; k!: KfromE<E>; t!: (typeof TOKEN)[KfromE<E>]; p!: (typeof PRECISIONS)[KfromE<E>]; a!: (typeof CONTRACT_ADDRESS)[KfromE<E>]; } 。两者都应该起作用。我本人更喜欢查找,因为它们不太依赖编译器“魔术”,但这取决于您。

希望有所帮助;祝你好运!

答案 1 :(得分:1)

我相信您可以使用infer关键字来做到这一点:

type InferToken<E> = E extends ERC20Tx<infer G, any, any> ? G : never;
type InferPrecision<E> = E extends ERC20Tx<any, infer G, any> ? G : never;
type InferAddress<E> = E extends ERC20Tx<any, any, infer G> ? G : never;

class ETH2ERC20<E extends ERC20Tx<TOKEN, PRECISIONS, CONTRACT_ADDRESS>, T = InferToken<E>, P = InferPrecision<E>, A = InferAddress<E>> { }

// Has type: ETH2ERC20<ERC20Tx<TOKEN.OMG, PRECISIONS.OMG, CONTRACT_ADDRESS.OMG>, TOKEN.OMG, PRECISIONS.OMG, CONTRACT_ADDRESS.OMG>
const a = new ETH2ERC20<ERC20Tx<TOKEN.OMG, PRECISIONS.OMG, CONTRACT_ADDRESS.OMG>>();

// or

const b = new ETH2ERC20<OMG>();