【问题标题】:Create a TypeScript Record where the second type argument depends on the first one?创建一个 TypeScript 记录,其中第二个类型参数取决于第一个?
【发布时间】:2019-10-25 16:33:39
【问题描述】:

我正在尝试在 TS 中为将字符串(我们称之为 key)映射到其中一个参数始终是 key 的函数的对象定义一个类型。

我目前拥有的是一条记录,其中有一个用于 K 的 string 和一个用于值的函数,如下所示:

const IsCountry: Record<string, (name: string) => boolean> = {
  "US": (name) => true,
  "Caracas": => (name) => false
}

这很好用,但我想看看我是否可以通过让 TS 确定函数中的 name 应该只是 key 字符串来进一步缩小范围,以便类型检查器会抛出警告或如果我尝试传递其他任何内容,则会出错:

IsCountry["US"]("Caracas"); //SHOULD error out, but it doesn't 

在当前设计下该调用不会出错,因为函数的参数可以采用任何string

谢谢!

【问题讨论】:

  • 您希望IsCountry["US"]("US"); 正常,而其他参数出错?这是你真正需要的吗?
  • 确实听起来像XY problem。如果您通过IsCountry["US"] 中的属性名称获取某些内容,您应该已经知道该属性是什么。为什么需要重复相同的字符串作为函数的参数?

标签: typescript


【解决方案1】:

可以使用mapped types。但是我们需要一个类型来表示可能的键。下面我创建了字符串类型的联合 - Countries 并且类型被缩小了,因此只需要使用确切的键调用函数。

type CountryRecord<A extends string> = {
  [Key in A]: (x:Key) => boolean // mapped key - function argument needs to match the key
}
// type which defines all countries keys
type Countries = "US" | "Caracas";

const IsCountry: CountryRecord<Countries> = {
  "US": (name) => true,
  "Caracas": (name) => false
}
IsCountry["US"]("Caracas") // error
IsCountry["US"]("US") // correct 

另外,如果定义静态列表有问题,那么我们可以从现有结构创建一个类型,也许你有一些带有这些国家键的对象。下面的这种结构示例可以作为我们的类型蓝图:

const Countries = {
  "US": "United States of America",
  "Caracas": "Caracas"
}

type Countries = keyof typeof Countries; // type created from existing structure
// now the same usage:

const IsCountry: CountryRecord<Countries> = {
  "US": (name) => true,
  "Caracas": (name) => false
}

编辑

正如@jcalz 在评论中正确提到的那样,可以通过正确应用类型的标识函数来实现此效果。考虑:

type CountryRecord<T> = { [K in keyof T]: (name: K) => boolean };
const asCountryRecord = <T extends CountryRecord<T>>(t: T) => t; // identity function which applies the wanted type by inferencing the given structure
const IsCountry = asCountryRecord({
    "US": (name) => true,
    "Caracas": (name) => false
});

让我们解释一下这个身份函数是如何工作的:

const asCountryRecord = <T extends CountryRecord<T>>(t: T) => t; 
  • &lt;T extends CountryRecord&lt;T&gt;&gt; - T 是可分配给 CountryRecord 的类型,所以它确实需要有结构伪代码 - {key: key=&gt;bool}
  • (t: T) - 参数是一个类型为 T 的东西,所以这意味着 TS 将对此处给出的结构进行推断,并且 T 将等于参数中的 typeof 值。
  • =&gt; t - 这意味着函数什么都不做,只返回相同的数据,这就是为什么我说它只是运行时的身份。

换句话说,asCountryRecord 是一个工具,它可以使用参数的推断来实现泛型类型参数。解决方案与前面的命题相同,但不是静态现有类型,而是从参数推断类型。

【讨论】:

  • 您不需要预定义的类型或静态列表;相反,您可以使用像 this 这样的辅助函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-13
  • 2022-01-06
  • 2015-08-09
  • 2011-03-05
  • 1970-01-01
  • 2021-08-14
  • 1970-01-01
相关资源
最近更新 更多