创建一个TypeScript记录,其中第二个类型参数取决于第一个参数?

时间:2019-10-24 19:44:14

标签: typescript

我试图在TS中定义一个对象类型,该对象将一个字符串(我们称其为--- all: children: dmz1: hosts: myamqdev01.company.net: nodeId: 1 myamqdev02.company.net: nodeId: 2 mysmxdev01.company.net: nodeId: 1 mysmxdev02.company.net: nodeId: 2 intranet: hosts: amqintradev01.company.net: nodeId: 1 amqintradev02.company.net: nodeId: 2 smxintradev01.company.net: nodeId: 1 smxintradev02.company.net: nodeId: 2 amq: hosts: myamqdev01.company.net: myamqdev02.company.net: amqintradev01.company.net: amqintradev02.company.net: smx: hosts: mysmxdev01.company.net: mysmxdev02.company.net: smxintradev01.company.net: smxintradev02.company.net: )映射到一个参数始终为$ # All machines $ ansible -i dev/ --list-hosts all hosts (8): myamqdev01.company.net myamqdev02.company.net mysmxdev01.company.net mysmxdev02.company.net amqintradev01.company.net amqintradev02.company.net smxintradev01.company.net smxintradev02.company.net $ # Intranet $ ansible -i dev/ --list-hosts intranet hosts (4): amqintradev01.company.net amqintradev02.company.net smxintradev01.company.net smxintradev02.company.net $ # all smx machines $ ansible -i dev/ --list-hosts smx hosts (4): mysmxdev01.company.net mysmxdev02.company.net smxintradev01.company.net smxintradev02.company.net $ # amq machines only on dmz1 $ # 1. Only whith patterns $ ansible -i dev/ --list-hosts 'amq:&dmz1' hosts (2): myamqdev01.company.net myamqdev02.company.net $ # 2. Using limit $ ansible -i dev/ --list-hosts amq -l dmz1 hosts (2): myamqdev01.company.net myamqdev02.company.net 的函数。

我目前拥有的一条记录具有一个key的K和一个值的函数,如下所示:

key

哪个可以正常工作,但是我想看看是否可以通过使TS弄清楚函数中的string应该仅是它们的const IsCountry: Record<string, (name: string) => boolean> = { "US": (name) => true, "Caracas": => (name) => false } 字符串来进一步缩小范围,以便类型检查器将如果我尝试通过其他任何警告,则会引发警告或错误:

name

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

谢谢!

1 个答案:

答案 0 :(得分:1)

可以使用mapped types。但是我们需要有一个表示可能的键的类型。下面,我创建了字符串类型的并集-winsound.PlaySound,并且类型变窄,因此仅需使用确切的键来调用函数。

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 

编辑

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

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
}

让我们解释一下此身份功能的工作原理:

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是可分配给CountryRecord的类型,因此实际上它需要具有所需的结构伪代码-<T extends CountryRecord<T>>
  • {key: key=>bool}-参数是类型T的事物,因此TS会推断此处给出的结构,T将等于参数中的typeof值。
  • (t: T)-这意味着该函数不执行任何操作,仅返回相同的数据,这就是为什么我说它仅是运行时的身份。

换句话说,=> t是一种工具,它可以使用自变量的推论来实现通用类型自变量。解决方案与先前的命题相同,但是从参数中推断出类型,而不是静态的现有类型。