任意泛型的打字稿图 [英] Typescript Map of Arbitrary Generics

查看:46
本文介绍了任意泛型的打字稿图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试定义两种类型,它们的外观应类似于:

I'm trying to define two types, which should look something like:

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
};

我要表达的约束是 params key 的两个参数具有相同的类型,而QueryMap只是来自字符串的映射到任意 IQuery (类型无关).编译器在这里抱怨是因为它希望为 IQuery 指定一种类型,但是要点是,映射中的每个 IQuery 应该独立地参数化.有什么办法可以在打字稿中表达这一点?

The constraints I'm trying to express are that params and key have the same types for their two arguments, and that a QueryMap is just a mapping from a string to an arbitrary IQuery (doesn't matter what the types are). The compiler complains here because it wants a type to be specified for IQuery, but the point is that each IQuery in the map should be independently parameterized. Is there any way to express this in typescript?

此外,如果可能的话,我想获取有关遍历此树的 IQuery 中存在的上游 QueryMap 形状的信息/保证.

Additionally, if possible, I'd like to get information/guarantees about the shape of the upstream QueryMaps present in the IQuery as I iterated through this tree.

推荐答案

您可以做的最简单事情是这样的:

The simplest thing you can do is this:

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

它不是完全类型安全的,但是与您要表示的内容相差不远.如果您不想丢失类型为 QueryMap 的值的类型信息,请允许编译器推断出较窄的类型,并使用通用帮助函数来确保其为有效的 QueryMap ,就像这样:

It's not completely type-safe, but it is not too far off what you're trying to represent. If you don't want to lose type information for a value of type QueryMap, allow the compiler to infer a narrower type and use a generic helper function to ensure it is a valid QueryMap, like this:

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'] .

The value queryMap.foo.params is still known to be a method that accepts a string and an optional number, even though the type QueryMap['foo']['params'] isn't.

如果您指定了不能分配给 QueryMap 的内容,则会出现错误:

If you specify something not assignable to a QueryMap you will get an error:

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


此处显示的不是完全类型安全的问题:


The not-completely type-safe problem is shown here:

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

即使没有一致的合理的 P U 值在这里都可以使用,这是可以接受的(当您使用 any 时会发生这种情况>).如果您需要进一步锁定此值,可以尝试让TypeScript从值中推断出 P U 值的集合,或者如果不能,则警告您,但这不是明智之举.

This is accepted, even though there's no consistent reasonable values of P and U that works here (which is what happens when you use any). If you need to lock this down more, you can try to have TypeScript infer sets of P and U values from the value or warn you if it cannot, but it's not staightforward.

出于完整性考虑,这是我的操作方法...使用条件类型通过检查 params来为 QueryMap 的每个元素推断 P U 方法,然后验证 key 方法是否与之匹配.

For completeness, here's how I'd do it... use conditional types to infer P and U for each element of your QueryMap by inspecting the params method, and then verify that the key method matches it.

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;

现在,以下内容仍然有效:

Now the following will still work:

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()的类型,所以我不知道这是值得的. IQuery<任何,任何> 可能足以满足大多数目的.

This increases your type safety marginally at the expense of a fairly complicated bit of type juggling in the type of asSaferQueryMap(), so I don't know that it's worth it. IQuery<any, any> is probably good enough for most purposes.

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

Okay, hope that helps; good luck!

这篇关于任意泛型的打字稿图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆