以下这些情况是接受和不可接受的声明。
export class MyComponent{
error: 'test' = 'test'; // accept
error: 'test' = 'test1'; // not accept
error: Boolean = true || false; // accept
error: true | false = true; // not accept
error: true = true; // accept
error: true = false; // not accept
error: Boolean; //accept
error: true; // accept
error: 1 = 1; //accept
error: 1 = 2; // not accept
}
- 为什么TypeScript允许将值作为数据类型?
- JavaScript如何在编译时处理这些?
- 它与
readonly
和constant
的区别如何?
readonly error= 'test';
与error: 'test' = 'test';
答案 0 :(得分:10)
首先,一些非正式的背景和信息将帮助我们讨论您提出的问题:
通常,类型表示一组0或更多值。这些价值观可以被认为是那种类型的成员或居民。
就他们可以采用的这些多样性而言,类型往往属于3组中的一组。
第1组:案例:string
类型。 string
类型居住在所有字符串值中。由于字符串可以有效地任意长,因此基本上存在无限数值,它们是string
类型的成员。作为此类型成员的值集是所有可能字符串的集合。
第2组:案例:undefined
类型。 undefined
类型只有一个值undefined
。因此,此类型通常称为单例类型,因为其成员集只有1个值。
第3组:案例:never
类型。 never
类型没有成员。根据定义,不可能具有never
类型的值。当您在专业人士中阅读它时,这看起来有点令人困惑,但是一个小代码示例可以解释它。
考虑:
function getValue(): never {
throw Error();
}
在上面的示例中,函数getValue
的返回类型为never
,因为从不返回一个值,它总是抛出。因此,如果我们写
const value = getValue();
value
也属于never
类型。
现在转到第一个问题:
为什么打字稿允许将值作为数据类型?
有许多原因,但有一些特别引人注目的原因
根据传递给它们的值来模拟行为不同的函数的行为。想到的一个例子是函数document.getElementsByTagName
。此函数始终采用string
类型的值,并始终返回包含NodeList
的{{1}}。但是,根据传递的实际字符串值,它将返回该列表中完全不同类型的事物。这些元素之间唯一的共同点是它们都来自HTMLElement
。
现在,让我们考虑如何写下此函数的类型签名。我们的第一次尝试可能就像
HTMLElement
这是正确的,但并不是特别有用。想象一下,我们想要获取页面上所有HTMLInput元素的值,以便我们将它们发送到我们的服务器。
我们知道,declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
实际上只返回页面上的输入元素,正是我们想要的,但是我们上面的定义,当然我们得到正确的值(TypeScript不影响JavaScript运行时行为) ),他们会有错误的类型。具体来说,它们的类型为getElementsByTagName('input')
,是HTMLElement
的超类型,没有我们想要访问的HTMLInputElement
属性。
那我们该怎么办?我们可以&#34;演员&#34;所有返回的元素都归value
但这很丑陋,容易出错(我们必须记住所有的类型名称以及它们如何映射到它们的标记名称),详细,有点迟钝,我们知道的更好,而且我们更了解静态。
因此,需要对HTMLInputElement
的值之间的关系进行建模,该值是tagname
的参数和它实际返回的元素类型。
输入字符串文字类型:
字符串文字类型是一种更精炼的字符串类型,它是单例类型,就像getElementsByTagName
一样,它只有一个值,即文字字符串。一旦我们拥有这种类型,我们就可以重载 undefined
的声明,使其更加精确和有用
getElementsByTagName
我认为这清楚地证明了具有专门的字符串类型的效用,这些字符串类型是从一个值中获得的,并且只存在于该单个值中,但是还有很多其他原因可以使用它们,所以我将再讨论一些。
在前面的例子中,我会说易用性是主要动机,但请记住TypeScript的#1目标是通过编译时,静态分析来捕获编程错误。
鉴于此,另一个动机是精确度。有许多JavaScript API具有特定的价值,并且取决于它们是什么,它们可能会做一些非常不同的事情,什么都不做,或者很难做到。
因此,对于另一个真实示例, SystemJS 是一个优秀且广泛使用的模块加载器,具有广泛的配置API。您可以传递给它的其中一个选项称为declare function getElementsByTagName(tagname: 'input'): NodeList<HTMLInputElement>;
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
,并且根据您指定的值,将发生非平凡的不同事情,此外,如果您指定了无效值,它将尝试加载一个模块,不存在并且无法加载任何其他内容。
transpiler
的有效值为:transpiler
,"plugin-traceur"
,"plugin-babel"
和"plugin-typescript"
。我们不仅希望TypeScript提出这4种可能性,还要检查我们是否只使用其中的一种。
在我们将离散值用作类型之前,这个API很难建模。
充其量,我们必须写类似
的内容false
这不是我们想要的,因为只有3个有效字符串且transpiler: string | boolean;
不是有效值!
通过将值用作类型,我们实际上可以完美地描述此API
true
不仅知道我们可以传递哪些值,而且如果我们错误地输入transpiler: 'plugin-traceur' | 'plugin-babel' | 'plugin-typescript' | false;
或尝试传递'plugin-tsc'
,则会立即收到错误。
因此,字面类型可以及早捕获错误,同时实现Wilds中现有API的精确描述。
另一个好处是控制流分析,它允许编译器检测常见的逻辑错误。这是一个复杂的主题,但这是一个简单的例子:
true
上面的代码包含一个相对简单的逻辑错误,但是由于复杂的条件和各种人为因素,在实践中很容易错过。第三个declare const compass: {
direction: 'N' | 'E' | 'S' | 'W'
};
const direction = compass.direction;
// direction is 'N' | 'E' | 'S' | 'W'
if (direction === 'N') {
console.log('north');
} // direction is 'E' | 'S' | 'W'
else if (direction === 'S') {
console.log('south');
} // direction is 'E' | 'W'
else if (direction === 'N') { // ERROR!
console.log('Northerly');
}
本质上是死代码,它的主体永远不会被执行。文字类型授予我们的特殊性,可以将可能的指南针if
声明为&#39; N&#39; S&#39; S&#39; E&#39; E&#39;或者&# 39; W&#39;使编译器能够立即将第三个if语句标记为无法访问,实际上是无意义的代码,表示我们程序中存在错误,这是一个逻辑错误(毕竟我们只是人类)。
因此,我们还有一个主要的激励因素,能够定义与非常特定的可能值子集相对应的类型。最后一个例子中最好的部分就是我们自己的代码。我们想宣布一份合理但非常具体的合同,这种语言为我们提供了表达能力,然后在我们违反合同时抓住了我们。
Javascript如何在编译时处理它们?
与所有其他TypeScript类型完全相同。它们完全从TypeScript编译器发出的JavaScript中删除。
它与readonly和constant的区别如何?
与所有TypeScript类型一样,您所说的类型,指示特定值的类型,与direction
和const
修饰符互动。这种互动有点复杂,可以浅谈,就像我在这里所做的那样,或者很容易构成一个Q / A本身。
可以这么说,readonly
和const
对可能的值有影响,因此变量或属性可以随时实际保存的可能类型因此产生文字类型,类型是特定值的类型,更容易传播,推理,也许最重要的是推断我们。
因此,当某些东西是不可变的时,通常有必要推断它的类型尽可能具体,因为它的值不会改变。
readonly
将const x = 'a';
的类型推断为x
,因为无法重新分配。
'a'
另一方面,将let x = 'a';
的类型推断为x
,因为它是可变的。
现在你可以写
string
在这种情况下,虽然它是可变的,但只能为其指定let x: 'a' = 'a';
类型的值。
请注意,出于说明目的,这有点过于简单化了。
在上面的'a'
示例中可以观察到其他机制正在运行,这表明该语言有另一个层,即控制流分析层,它在缩小值时跟踪可能的值类型通过条件,赋值,真理检查和其他构造,如解构赋值。
现在让我们详细检查你问题中的类,属性:
if else if
TypeScript与类型推断有关,推理越多越好,因为它能够传播来自值的类型信息,例如表达式,并使用它来推断更精确的类型。
在大多数语言中,类型系统以类型开始,但在TypeScript中,并且几乎一直都是这种情况,类型系统以值开头。所有值都有类型。对这些值的操作会产生新值,新类型允许类型干扰进一步传播到程序中。
如果将纯JavaScript程序粘贴到TypeScript文件中,您会注意到,如果不添加任何类型的注释,它就能够详细了解程序的结构。文字类型进一步增强了这种能力。
关于文字类型还有很多可以说的,为了解释的目的,我已经省略并简化了某些事情,但请放心,它们很棒。
答案 1 :(得分:2)
为什么TypeScript接受一个值作为数据类型?
这是字符串文字类型的扩展,这个PR解释了它:literal types
JavaScript如何在编译时处理这些?
它的纯打字稿创建,不会影响生成的javascript。
它与readonly和constant有什么不同?
嗯 - 它不会只读。它只允许一个值。检查此示例:
export class MyComponent
{
readonly error = 1;
error1: 1 = 1;
public do()
{
this.error = 1; //Error. The field is readonly
this.error1 = 1; //No error - because the field is not readonly
this.error1 = 2; //Error. Type mismatch
}
}
答案 2 :(得分:1)
一个原因是为同一个变量处理多个类型。这就是为什么typescript允许你使用类型的特定值。
let x: true | false | 'dog';
x = true; // works
x = false; // works
x = 'cat'; // compilation error
在这种情况下,let x: true
只是一种只有一种类型的特殊情况。
看起来字符串文字类型功能也被释放以允许其他类型的值。也许有更好的文档示例,但我能找到的只是手册here中的字符串文字类型部分。