我试着定义两种类型,应该是:

export type IQuery<P, U>  = {
  request: string;
  params: (props: P, upsteam?: U) => object;
  key: (props: P, upstream?: U) => string;
  forceRequest: boolean;
  depends?: QueryMap
}

export type QueryMap = {
  [k: string]: IQuery
};

我试图表达的约束是,paramskey对于它们的两个参数具有相同的类型,而QuyYMAP只是从一个字符串到任意的IQuery的映射(不管这些类型是什么)。编译器在这里抱怨是因为它希望为IQuery指定一种类型,但要点是地图中的每个IQuery应独立参数化。有没有办法用字体来表达?
另外,如果可能的话,我想在遍历此树时获得关于QueryMap中存在的上游IQuerys形状的信息/保证。

最佳答案

你能做的最简单的事情是:

export type QueryMap = {
  [k: string]: IQuery<any, any>
};

它并不完全是类型安全的,但它与您所要表示的并不太远。如果不想丢失QueryMap类型值的类型信息,请允许编译器推断较窄的类型,并使用泛型帮助函数确保它是有效的QueryMap,如下所示:
const asQueryMap = <T extends QueryMap>(t: T) => t;

const queryMap = asQueryMap({
  foo: {
    request: "a",
    params(p: string, u?: number) { return {} },
    key(p: string, u?: number) { return "hey" },
    forceRequest: true
  }
});

queryMap.foo.params仍然是一个接受string和可选number的方法,即使类型QueryMap['foo']['params']不是。
如果您指定了一些不可分配给QueryMap的内容,您将得到一个错误:
const bad = asQueryMap({
  foo: {
    request: "a",
    params(p: string, u?: number) { return {} },
    key(p: string, u?: number) { return "hey" },
    forceRequest: true
  },
  bar: {
    request: 123,
    params(p: number, u?: string) {return {}},
    key(p: number, u?: string) {return "nope"},
    forceRequest: false
  }
}); // error! bar.request is a number

此处显示了不完全类型安全问题:
const notExactlySafe = asQueryMap({
  baz: {
    request: "a",
    params(p: number, u?: string) { return {} },
    key(p: string, u?: number) { return "hey" },
    forceRequest: true
  }
});

这是可以接受的,即使这里没有一致的PU合理值(这就是使用any时发生的情况)。如果您需要进一步锁定此值,可以尝试让typescript从该值推断PU值集,如果不能,则警告您,但它不是固定的。
为了完整起见,我会这样做…使用conditional types通过检查P方法来推断UQueryMap对于params的每个元素,然后验证key方法是否与之匹配。
const asSaferQueryMap = <T extends QueryMap>(
  t: T & { [K in keyof T]:
    T[K]['params'] extends (p: infer P, u?: infer U) => any ? (
      T[K] extends IQuery<P, U> ? T[K] : IQuery<P, U>
    ) : never
  }
): T => t;

现在,以下操作仍然有效:
const queryMap = asSaferQueryMap({
  foo: {
    request: "a",
    params(p: string, u?: number) { return {} },
    key(p: string, u?: number) { return "hey" },
    forceRequest: true
  }
});

虽然现在这将是一个错误:
const notExactlySafe = asSaferQueryMap({
  baz: {
    request: "a",
    params(p: number, u?: string) { return {} },
    key(p: string, u?: number) { return "hey" },
    forceRequest: true
  }
}); // error, string is not assignable to number

这就增加了你的类型安全性,而牺牲了一个相当复杂的类型——类型的杂耍,所以我不知道它是值得的。asSaferQueryMap()在大多数情况下可能已经足够好了。
好吧,希望能帮上忙;祝你好运!

10-07 14:56