首页 > 解决方案 > 创建一个 TypeScript 记录,其中第二个类型参数取决于第一个?

问题描述

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

我目前拥有的是一个记录,它有一个stringfor K 和一个函数的值,如下所示:

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.

谢谢!

标签: typescript

解决方案


可以通过使用映射类型来实现。但是我们需要一个类型来表示可能的键。下面我创建了字符串类型的联合 -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; 
  • <T extends CountryRecord<T>>- T 是可分配给 CountryRecord 的类型,因此它确实需要具有结构伪代码 -{key: key=>bool}
  • (t: T)- 参数是一个类型为 T 的事物,因此这意味着 TS 将对此处给出的结构进行推断,并且 T 将等于参数中的 typeof 值。
  • => t- 这意味着该函数什么都不做,只返回相同的数据,这就是为什么我说它只是运行时的标识。

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


推荐阅读