可以使用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;
-
<T extends CountryRecord<T>> - T 是可分配给 CountryRecord 的类型,所以它确实需要有结构伪代码 - {key: key=>bool}
-
(t: T) - 参数是一个类型为 T 的东西,所以这意味着 TS 将对此处给出的结构进行推断,并且 T 将等于参数中的 typeof 值。
-
=> t - 这意味着函数什么都不做,只返回相同的数据,这就是为什么我说它只是运行时的身份。
换句话说,asCountryRecord 是一个工具,它可以使用参数的推断来实现泛型类型参数。解决方案与前面的命题相同,但不是静态现有类型,而是从参数推断类型。