Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么TypeScript';编译器不分析类型数组吗?_Typescript - Fatal编程技术网

为什么TypeScript';编译器不分析类型数组吗?

为什么TypeScript';编译器不分析类型数组吗?,typescript,Typescript,设置:给定一些类型脚本代码,如: type ObjectList = { [index: string]: string; }; function makeList(input: ObjectList | string | number): string[] { if (typeof input === "string" || typeof input === "number") { return [String(input)]; }

设置:给定一些类型脚本代码,如:

type ObjectList = {
  [index: string]: string;
};

function makeList(input: ObjectList | string | number): string[] {
  if (typeof input === "string" || typeof input === "number") {
    return [String(input)];
  }
  const arr = [];
  for (const x in input) {
    arr.push(String(input[x]));
  }
  return arr;
}

// Turns a number into a string array
console.log(makeList(123));

// Turns a string into an array
console.log(makeList("hello"));

// Turns an object dictionary into an array
console.log(makeList({ a: "A", b: "B" }));
好的,很简单,而且效果很好。这条线对类型安全至关重要:

if (typeof input === "string" || typeof input === "number") {
它确保了中的
只在
对象列表
上运行,而TypeScript编译器为我们做了大量的工作!这里的缺点是这行代码有多冗长,特别是如果您要添加更多类型,或者我们应该能够做到这一点:

if (['string', 'number'].includes(typeof input)) {
但是,这将导致TypeScript编译器错误,基本上表明
for循环
不确定
字符串
数字
类型无法到达它

“for…in”语句的右侧必须为“any”类型、对象类型或类型参数,但此处的类型为“string | number | ObjectList”

下面是一个运行示例:


问题:为了更好地理解编译器,我对编译器为何表现出这种行为感兴趣。那么
[].includes()
与编译器不兼容呢?静态分析是否太深?有没有我没有想到的运行时注意事项?当用于比较时,TS中的
typeof
运算符是否还有一些附加的“魔力”?

这在评论中提到或暗示过,但为了完整性,我想扩展成一个答案


当您直接检查输入的类型===“string”
时,编译器将其视为类型保护,这对
输入的外观类型有影响。而在检查之前,
输入
仅为其声明的类型(例如,
ObjectList | string | number
),在检查之后,编译器可以在代码区域中为其提供更窄、更明显的类型,只有在检查返回
true
(例如,
string
)时才能达到该类型,在代码区域中的另一种明显类型,只有当检查返回
false
(例如
ObjectList | number
)时才能到达。这就是所谓的

通过模拟代码在所有可能输入上的行为并给出
输入
所有可能值的并集,这种控制流分析不起作用。它也不能通过直觉和智力工作;编译器无法简单地“理解”当且仅当某些变量比它们声明的类型窄时,某些代码块是可访问的。取而代之的是,已经实现了一系列特定的启发式,对应于已知用作类型保护的不同的可识别和公共编码模式

typeof someVariable==“string”
视为类型保护就是这样一种启发

但是
[“string”].includes(typeof someVariable)
未被编译器视为类型保护;没有人实施过这样的事情


关于类似问题/建议的有趣讨论可以在以下位置找到:为什么TypeScript不将
Array.includes()
Array.indexOf()
视为类型保护

正如@RyanCavanaugh(微软TypeScript团队的开发负责人)所说:

[缺少类型保护]是指缺少功能实现的行为,该功能将导致其按照OP建议的方式运行。现有的收缩机制都不适用于此;我们讨论的可能是一千行新代码,以基于这些方法正确检测和缩小数组,以及每个程序所付出的性能代价,因为控制流图必须更精细,以设置任何方法调用所导致的可能缩小,还有一个bug跟踪和混乱跟踪,这是由于人们期望这些函数的非方法版本也以同样的方式运行

我想这个案子也是如此;在编译器中实现这种类型保护将花费大量的工作,使编译器对每个人都变慢,并引入更多的复杂性和可能的bug,所有这些都是为了支持一个相对不常见的用例


幸运的是,TypeScript确实让开发人员能够通过编写自己的自定义类型保护。您需要将预期的类型保护重构为一个作用于其参数之一的
布尔返回函数,这意味着您仍然无法获得
[“string”]。includes(typeof input)
,以实现开箱即用,但您至少可以更进一步

下面是一个可能的实现:

interface TypeofMap {
  string: string;
  number: number;
  bigint: bigint;
  boolean: boolean;
  symbol: symbol;
  undefined: undefined;
  object: object | null;
  function: Function
}
function typeofIncludes<K extends keyof TypeofMap>(typeofs: K[], val: any): val is TypeofMap[K];
function typeofIncludes(typeofs: Array<keyof TypeofMap>, val: any) {
  return typeofs.includes(typeof val);
}
这很有效。在
if(typeofIncludes([“string”,“number”],input))
的“then”子句中,
input
已缩小为
string | number
,而在“else”子句中,它已缩小为
ObjectList



评论中提到或暗指了这一点,但为了完整起见,我想进一步给出答案


当您直接检查输入的类型===“string”
时,编译器将其视为类型保护,这对
输入的外观类型有影响。而在检查之前,
输入
仅为其声明的类型(例如,
ObjectList | string | number
),在检查之后,编译器可以在代码区域中为其提供更窄、更明显的类型,只有在检查返回
true
(例如,
string
)时才能达到该类型,在代码区域中的另一种明显类型,只有当检查返回
false
(例如
ObjectList | number
)时才能到达。这就是所谓的

通过模拟代码在所有可能输入上的行为并给出
输入
所有可能值的并集,这种控制流分析不起作用。它也不能通过直觉和智力工作;编译器无法简单地“理解”某些代码块是rea
function makeList(input: ObjectList | string | number): string[] {
  if (typeofIncludes(["string", "number"], input)) {
    return [String(input)];
  }
  const arr = [];
  for (const x in input) {
    arr.push(String(input[x]));
  }
  return arr;
}