为什么TypeScript接受一个值作为数据类型?

时间:2017-06-15 07:13:30

标签: angular typescript variables types typescript-typings

为什么TypeScript接受一个值作为数据类型?

以下这些情况是接受和不可接受的声明。

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如何在编译时处理这些?
  •   
  • 它与readonlyconstant的区别如何?
  •   

readonly error= 'test';error: 'test' = 'test';

3 个答案:

答案 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类型一样,您所说的类型,指示特定值的类型,与directionconst修饰符互动。这种互动有点复杂,可以浅谈,就像我在这里所做的那样,或者很容易构成一个Q / A本身。

可以这么说,readonlyconst对可能的值有影响,因此变量或属性可以随时实际保存的可能类型因此产生文字类型,类型是特定值的类型,更容易传播,推理,也许最重要的是推断我们。

因此,当某些东西是不可变的时,通常有必要推断它的类型尽可能具体,因为它的值不会改变。

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中的字符串文字类型部分。