通用api类型的打字稿?

时间:2018-08-30 18:45:40

标签: typescript

例如,api像这样的api('serviceName', {data: 1}),由api的客户端调用。请注意,客户端不必与服务器在同一台计算机上运行。

api如下:

export const api = async (type: string, payload: Object) => {
  const res: any = await request
    .post(`someurl.com/api`)
    .json({data: ejson.stringify({type, payload})});
  return res;
};

服务器端看起来像这样:

const apiRemote = (apiName, apiInput) => {
  return apiList[apiName](apiInput)
}

请注意,客户端不能直接包含apiRemote文件。

如何使api具有类型?

类似,但流类型为:https://flow.org/try/#0MYewdgzgLgBBCmAnAbgS2PCAWGBeGA3gFAwwCGAXORAJ5jAwAUZiA5hFQQBbw1VgBXALYAjJAF8AlFQAKiEENQIAPAUoxBoiQD48u4qVKJ4UAYjCESh0uoDMOAFTk2EAHQ8aAGiulxAbitxb1IRKjJaeiYWdk4eABs4kCpoRFQwVilZeUUVAlC4KFT08V1cfR8YY1NzS2sQqgADCAATZoaYAGpndnd4BJBgw39A72GiAHpxmCguJRgAMwF6KFRwGDmwEFhWEDTWaZAYMRgBBGajmmmeGAAHeQAreGBYOfvT2HmQRBgJqfnEgDurhgACFwugyAlLjM5nMyEdUs1WPAjiYAfB4BZgHFUJjYBBUM0UWQwOcECgkHBCfBXL8YAB1LiHYCQuIQK6YFEshLsuZrGZcnF4qlEzxHASwAUIBZLZ6rSB08BxS6IJYwfnXcnISkEom00CQD5gHD4cJ0BjMBIAQRcVAA8iJHs9JHpapUTGYLFr0JgsABtVk2noAa14AF1LXEg25ohBJAExkRmBELS6ym6DdAYMhIQJ4Fa8OQAWRUEasIwCKG+DAGmQGmLY7FeFQsOIpAFSIwc3E81bXOpNGJEPGYJM4EyBHFzgA5O0AFQWJbiVi7ufz-eShT2I7HEAnU8XqGXpCsmdg3bzIMLZGLpYWxorVcaInr3Q4hHiiUaEAgDTb8ZXC94BBVx8hSbc-FHKY9xAScZ3nQ9jyYICQPyQckB3aD93OeYlyIKRGHjIA

一些几乎可行的示例:

interface S1 {
  name: 's1',
  input: number,
  output: number,
}

interface S2 {
  name: 's2',
  input: string,
  output: string,
}

interface List {
  s1: S1;
  s2: S2;
}

const a = async () => {
  type Api = <T extends keyof List>(name: T, input: List[T]['input']) => List[T]['output'];
  const api: Api = window["api"];
  const x: number = await api("s1", 2);
  const y: string = await api("s2", 's');
};

问题是我不喜欢格式化S1和S2。我想看起来像这样:

type S1 = (input:Input):Output

或类似的东西。

编辑2:

interface S1 {
  name: 's1',
  input: number,
  output: number,
}

interface S2 {
  name: 's2',
  input: string,
  output: string,
}

type Service<T extends { input: any, output: any }> = (input: T['input']) => Promise<T['output']>

// I'd like not to have this one because now I have to enforce manually that
// s1 key in List is the same as S1["name"].
// I think makes sense for the name to be in the S1 not in this List.
interface List {
  s1: S1;
  s2: S2;
}

// I'd like to keep it in this format instead
type S = S1 | S2;

const a = async () => {
  // But in this place, if I replace `List[T]` with `S['input']` will lose the types.
  type Api = <T extends S["name"]>(name: T, input: List[T]['input']) => Promise<List[T]['output']>;
  const api: Api = window["api"];
  const x: number = await api("s1", 2);
  const y: string = await api("s2", 's');
};

1 个答案:

答案 0 :(得分:1)

interface List {
  s1: (input: number) => number;
  s2: (input: string) => string;
}

// https://github.com/Microsoft/TypeScript/pull/26243
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;

const a = async () => {
  type Api = <T extends keyof List>(name: T, ...input: Parameters<List[T]>) => ReturnType<List[T]>;
  const api: Api = window["api"];
  const x: number = await api("s1", 2);
  const y: string = await api("s2", 's');
};

第二回合

好的,如果您想这样做,可以使用以下代码从List构造S,然后像以前一样进行操作:

// Distributive conditional type to look at each union constituent of `S`
// and keep the one with the name we are looking for.
type Lookup<SS, K> = SS extends { name: infer N } ? N extends K ? SS : never : never;
type List = {
    [K in S["name"]]: Lookup<S, K>
};

有关条件类型的更多信息,请参见handbook

相关问题