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

Ram*_*ran 9 variables types typescript typescript-typings angular

为什么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
    }
Run Code Online (Sandbox Code Playgroud)
  • 为什么TypeScript允许将值作为数据类型?
  • JavaScript如何在编译时处理这些?
  • readonlyconstant?有什么不同?

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

Alu*_*dad 10

首先,一些非正式的背景和信息将帮助我们讨论您提出的问题:

通常,类型表示一组0或更多值.这些价值观可以被视为该类型的成员或居民.

就它们可以采用的这种多样性而言,类型往往落在3组中的1组中.

第1组:例证:string类型.该string类型由所有字符串值居住.由于字符串可以有效地任意长,因此基本上存在作为该string类型的成员的无限数值.作为此类型成员的值集是所有可能字符串的集合.

第2组:例证:undefined类型.该undefined类型只有一个值,即undefined值.因此,此类型通常称为单例类型,因为其成员集只有1个值.

第3组:例证:never类型.该never类型没有成员.根据定义,不可能具有类型的值never.当你在专业人士中阅读它时,这看起来有点令人困惑,但是一个小代码示例可以解释它.

考虑:

function getValue(): never {
  throw Error();
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,函数getValue具有返回类型,never因为它永远不会返回值,它总是抛出.因此,如果我们写

const value = getValue();
Run Code Online (Sandbox Code Playgroud)

value也将是类型never.

现在回答第一个问题:

为什么打字稿允许值作为数据类型?

有许多原因,但有一些特别引人注目的原因

根据传递给它们的值来模拟行为不同的函数的行为.想到的一个例子是功能document.getElementsByTagName.此函数始终采用type的值string,并始终返回NodeList包含HTMLElements的值.但是,根据传递的实际字符串值,它将返回该列表中完全不同类型的事物.这些元素之间唯一的共同点是它们都来源于HTMLElement.

现在,让我们考虑一下如何写下这个函数的类型签名.我们的第一次尝试可能是这样的

declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
Run Code Online (Sandbox Code Playgroud)

这是正确的,但它并不是特别有用.想象一下,我们想要获取页面上所有HTMLInput元素的值,以便我们将它们发送到我们的服务器.

我们知道getElementsByTagName('input'),实际上只返回页面上的输入元素,正是我们想要的,但是通过上面的定义,当我们获得正确的值(TypeScript不会影响JavaScript运行时行为)时,它们将具有错误的类型.具体来说,它们是类型HTMLElement,超类型HTMLInputElement没有value我们想要访问的属性.

所以,我们能做些什么?我们可以"转换"所有返回的元素,HTMLInputElement但这很丑陋,容易出错(我们必须记住所有的类型名称以及它们如何映射到它们的标记名称),冗长和一点点钝,我们知道的更好,我们静态地了解.

因此,变得需要将之间的关系进行建模tagname,这是参数getElementsByTagName,它实际上返回元件的类型.

输入字符串文字类型:

字符串文字类型是一种更精炼的字符串类型,它是单例类型,就像undefined它只有一个值一样,即文字字符串.一旦我们有这种类型的,我们能够超载的声明getElementsByTagName使得它优越准确和有用

declare function getElementsByTagName(tagname: 'input'): NodeList<HTMLInputElement>;
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
Run Code Online (Sandbox Code Playgroud)

我认为这清楚地证明了具有专门的字符串类型的效用,这些字符串类型是从一个值派生的,并且只有该单个值所居住,但是还有很多其他原因可以使用它们,所以我将再讨论一些.

在前面的例子中,我会说易用性是主要动机,但请记住,TypeScript的#1目标是通过编译时,静态分析来捕获编程错误.

鉴于此,另一个动机是精确性.有许多JavaScript API具有特定的价值,并且取决于它是什么,它们可以做一些非常不同的事情,什么都不做,或者很难.

因此,对于另一个真实示例,SystemJS是一个优秀且广泛使用的模块加载器,具有广泛的配置API.您可以传递给它的一个选项被调用,transpiler并且根据您指定的值,将发生非平凡的不同事情,此外,如果您指定了无效值,它将尝试加载不存在的模块并失败加载其他任何东西.对于有效值transpiler是:"plugin-traceur","plugin-babel","plugin-typescript",和false.我们不仅希望TypeScript能够提供这4种可能性,还要检查我们是否仅使用其中一种可能性.

在我们将离散值用作类型之前,此API很难建模.

充其量,我们必须写类似的东西

transpiler: string | boolean;
Run Code Online (Sandbox Code Playgroud)

这不是我们想要的,因为只有3个有效字符串且true不是有效值!

通过将值用作类型,我们实际上可以完美地描述此API

transpiler: 'plugin-traceur' | 'plugin-babel' | 'plugin-typescript' | false;
Run Code Online (Sandbox Code Playgroud)

并且不仅知道我们可以通过哪些值,而且如果我们错误地键入'plugin-tsc'或尝试通过则立即得到错误true.

因此,字面类型可以及早捕获错误,同时实现Wilds中现有API的精确描述.

另一个好处是控制流分析,它允许编译器检测常见的逻辑错误.这是一个复杂的主题,但这是一个简单的例子:

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');
}
Run Code Online (Sandbox Code Playgroud)

上面的代码包含一个相对简单的逻辑错误,但是由于复杂的条件和各种人为因素,在实践中很容易错过.第三个if基本上是死代码,它的主体永远不会被执行.文字类型允许我们将可能的指南针声明direction为"N","S","E"或"W"之一的特殊性使编译器能够立即将第三个if语句标记为无法访问,实际上是无意义的代码,表示我们程序中的错误,一个逻辑错误(毕竟我们只是人类).

因此,我们还有一个主要的激励因素,能够定义与可能值的非常特定的子集相对应的类型.最后一个例子中最好的部分就是我们自己的代码.我们想要宣布一个合理但非常具体的合同,这种语言为我们提供了表达能力,然后在我们违反合同时抓住了我们.

以及Javascript如何在编译时处理它们?

与所有其他TypeScript类型完全相同.它们完全从TypeScript编译器发出的JavaScript中删除.

它与readonly和constant有何不同?

与所有TypeScript类型一样,您所说的类型,指示特定值的类型,与constreadonly修饰符交互.这种相互作用有点复杂,可以浅谈,就像我在这里做的那样,或者很容易构成一个Q/A.

可以这么说,const并且readonly对可能的值有影响,因此对变量或属性在任何时候实际可以容纳的可能类型都有影响,因此使得文字类型,特定值类型的类型,更容易传播,推理,也许最重要的是推断我们.

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

const x = 'a'; 
Run Code Online (Sandbox Code Playgroud)

推断的类型x'a'因为它不能被重新分配.

let x = 'a';
Run Code Online (Sandbox Code Playgroud)

另一方面,推断的类型xstring因为它是可变的.

现在你可以写

let x: 'a' = 'a';
Run Code Online (Sandbox Code Playgroud)

在这种情况下,虽然它是可变的,但只能赋予类型的值'a'.

请注意,对于说明目的而言,这有点过于简单化了.

if else if上面的例子中可以观察到有其他机制在工作,这表明该语言有另一层,控制流分析层,它跟踪可能的值类型,因为它们通过条件,赋值,真值检查缩小,和其他构造如解构赋值.


现在让我们详细检查您的问题中的类,属性属性:

export class MyComponent {
  // OK because we have said `error` is of type 'test',
  // the singleton string type whose values must be members of the set {'test'}
  error: 'test' = 'test';
  // NOT OK because we have said `error` is of type 'test',
  // the singleton string type whose values must be members of the set {'test'}
  // 'test1' is not a member of the set {'test'}
  error: 'test' = 'test1';
  // OK but a word of Warning:
  // this is valid because of a subtle aspect of structural subtyping,
  // another topic but it is an error in your program as the type `Boolean` with a
  // capital "B" is the wrong type to use
  // you definitely want to use 'boolean' with a lowercase "b" instead.
  error: Boolean = true || false;
  // This one is OK, it must be a typo in your question because we have said that 
  // `error` is of type true | false the type whose values must
  // be members of the set {true, false} and true satisfies that and so is accepted
  error: true | false = true;
  // OK for the same reason as the first property, error: 'test' = 'test';
  error: true = true;
  // NOT OK because we have said that error is of type `true` the type whose values
  // must be members of the set {true}
  // false is not in that set and therefore this is an error.
  error: true = false;
  // OK this is just a type declaration, no value is provided, but
  // as noted above, this is the WRONG type to use.
  // please use boolean with a lowercase "b".
  error: Boolean;
  // As above, this is just a type, no value to conflict with
  error: true;
  // OK because we have said `error` is of type 1 (yes the number 1),
  // the singleton number type whose values must be members of the set {1}
  // 1 is a member of {1} so we are good to go
  error: 1 = 1;
  // NOT OK because we have said `error` is of type 1 (yes the number 1),
  // the singleton number type whose values must be members of the set {1}
  // 2 is NOT a member of {1} so this is an error.
  error: 1 = 2;
}
Run Code Online (Sandbox Code Playgroud)

TypeScript与类型推理有关,推理越多越好,因为它能够传播来自值的类型信息,例如表达式,并使用它来推断更精确的类型.

在大多数语言中,类型系统以类型开始,但在TypeScript中,这种情况几乎总是如此,类型系统以值开头.所有值都有类型.对这些值的操作会产生新值,新类型允许类型干扰进一步传播到程序中.

如果将纯JavaScript程序粘贴到TypeScript文件中,您会注意到,如果不添加任何类型的注释,它就能够找到很多关于程序结构的信息.文字类型进一步增强了这种能力.

关于文字类型还有很多可以说的,为了解释的目的,我已经省略并简化了某些事情,但请放心,它们很棒.

  • 真的,这是我期待的好答案:+1 (2认同)