需求

现在我需要定义一个类型,它要根据不同的 enum 来约束其中的参数。

比如要开发一个 function 它的功能是获取全平台的 API 接口实例,但是由于不同平台的 API 接口不一致,有些平台需要额外传递参数,因此期望这个 fucntion 的调用方式如下;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//  调用 macos 的接口
getAPIInstance({
  platform: PlatformEnum.MACOS,
  platformConfig: {
    token: "token_string",
  },
});

//  调用 window 的接口
getAPIInstance({
  platform: PlatformEnum.WINDOW,
  platformConfig: {
    version: "1.2.0",
  },
});

//  调用 android 的接口,这个不需要传递额外的参数
getAPIInstance({
  platform: PlatformEnum.ANDROID,
});

可以注意到每个平台需要传递的 platformConfig 都不一样,有些平台甚至不需要传递。因此要做同意的类型约束除非使用 泛型

1
2
3
4
5
6
getAPIInstance<PlatformEnum.WINDOW>({
  platform: PlatformEnum.WINDOW,
  platformConfig: {
    version: "1.2.0",
  },
});

这样虽然能解决问题,但是有点不是很优雅,我们希望根据传入的 platform 参数能自动推导出 platformConfig 的类型,下面看下如何实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
export enum PlatformEnum {
  MACOS = "MACOS",
  WINDOW = "WINDOW",
  LINUX = "LINUX",
  ANDROID = "ANDROID",
  IPHONE = "IPHONE",
  CHROME_BOOK = "CHROME_BOOK",
}

type PlatformConfigDataMap = {
  [PlatformEnum.MACOS]: {
    token: string;
  };
  [PlatformEnum.WINDOW]: {
    version: number;
  };
  [PlatformEnum.LINUX]: {
    publisher: {
      name: string;
      link: string;
    };
  };
  [PlatformEnum.ANDROID]: undefined;
  [PlatformEnum.IPHONE]: {
    token: string;
  };
  [PlatformEnum.CHROME_BOOK]: undefined;
};

export type PlatformConfigType = {
  [K in keyof PlatformConfigDataMap]: PlatformConfigDataMap[K] extends undefined
    ? {
        platform: K;
        platformConfig?: PlatformConfigDataMap[K];
      }
    : { platform: K; platformConfig: PlatformConfigDataMap[K] };
}[keyof PlatformConfigDataMap];

测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//  typescript 到这里会报错,提示需要下面的类型
//  { platform: PlatformEnum.MACOS; platformConfig: { token: string; };
getAPIInstance({
  platform: PlatformEnum.MACOS,
});

//  没有错误
getAPIInstance({
  platform: PlatformEnum.ANDROID,
});