为什么 TypeScript 接受值作为数据类型?
Why does TypeScript accept value as a data type?
为什么 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
}
- Why does TypeScript allow a value as a data type?
- How does JavaScript handle these at compile time?
- How does it differ from
readonly
and constant
?
readonly error= 'test';
对比 error: 'test' = 'test';
一个原因是要处理同一变量的多种类型。这就是为什么打字稿允许您为类型使用特定值。
let x: true | false | 'dog';
x = true; // works
x = false; // works
x = 'cat'; // compilation error
在这种情况下 let x: true
只是一种特殊情况,只有一种类型。
似乎扩展了字符串文字类型功能以允许其他类型的值。也许有更好的文档示例,但我只能找到手册中的字符串文字类型部分 here.
Why does TypeScript accept a value as a data type?
这是字符串文字类型的扩展,这个 PR 解释了它:literal types
How does JavaScript handle these at compile time?
它是纯 typescript 创建的,不会影响结果 javascript。
How does it differ from readonly and 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
}
}
首先,一些非正式的背景和信息将有助于我们讨论您提出的问题:
一般来说,一个类型表示一组 0 个或多个值。这些值可以被认为是该类型的成员或居民。
就他们可以取的值的多样性而言,类型往往属于 3 组中的 1 组。
第 1 组: 例证:string
类型。 string
类型包含所有字符串值。由于一个字符串实际上可以是任意长的,所以本质上有无限个数值是 string
类型的成员。作为此类型成员的值集是所有可能字符串的集。
第 2 组: 例证:undefined
类型。 undefined
类型只有一个值,即 undefined
值。因此,这种类型通常被称为单例类型,因为它的成员集只有 1 个值。
第 3 组: 例证:never
类型。 never
类型没有成员。根据定义,不可能有 never
类型的值。当您在 pros 中阅读它时,这似乎有点令人困惑,但一个小代码示例可以解释它。
考虑:
function getValue(): never {
throw Error();
}
在上面的例子中,函数 getValue
有一个 return 类型的 never
因为它 never return 是一个值,它总是抛出。因此,如果我们写
const value = getValue();
value
也将是 never
类型。
现在开始第一个问题:
Why typescript allow value as a data type?
有很多很多原因,但有几个特别引人注目的是
根据传递给它们的值对函数的行为进行建模。想到的一个例子是函数 document.getElementsByTagName
。此函数始终采用 string
类型的值,并且始终 return 是包含 HTMLElement
的 NodeList
。然而,根据传递给它的实际字符串值,它会 return 列表中完全不同类型的东西。这些元素之间唯一的共同点是它们都派生自 HTMLElement
.
现在,让我们考虑一下如何写下这个函数的类型签名。我们的第一次尝试可能类似于
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
这是正确的,但不是特别有用。想象一下,我们想要获取页面上所有 HTMLInput 元素的值,以便我们可以将它们发送到我们的服务器。
我们知道,getElementsByTagName('input')
,实际上return只是页面上的输入元素,正是我们想要的,但是有了我们上面的定义,我们当然得到了正确的值( TypeScript 不会影响 JavaScript 运行时行为),它们将具有错误的类型。具体来说,它们将是 HTMLElement
类型,HTMLInputElement
的超类型,没有我们想要访问的 value
属性。
那我们能做什么呢?我们可以 "cast" 所有 returned 元素到 HTMLInputElement
但这很丑陋,容易出错(我们必须记住所有类型名称以及它们如何映射到它们的标签名称),冗长,有点迟钝,我们知道得更多,我们知道得更多静态。
因此,需要对 tagname
的 值 之间的关系建模,这是 getElementsByTagName
的参数和它的元素类型实际上 returns.
输入字符串文字类型:
字符串字面量类型是一种更细化的字符串类型,它是单例类型,就像undefined
它只有一个值,即字面量字符串。一旦我们有了这种类型,我们就能够 重载 getElementsByTagName
的声明,使其更加精确和有用
declare function getElementsByTagName(tagname: 'input'): NodeList<HTMLInputElement>;
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
我认为这清楚地证明了具有专门的字符串类型的实用性,从一个值派生并且仅由该单个值居住,但还有很多其他原因需要它们,所以我将讨论更多。
在前面的示例中,我会说易用性是主要动机,但请记住,TypeScript 的第一目标是通过编译时、静态分析来捕获编程错误。
鉴于此,另一个动机是精度。有很多很多 JavaScript API 取一个特定的值,根据它是什么,它们可能会做一些非常不同的事情,什么都不做,或者会失败。
所以,对于另一个真实世界的例子,SystemJS 是一个优秀且广泛使用的模块加载器,具有广泛的配置 API。您可以传递给它的选项之一称为 transpiler
,根据您指定的值,会发生非常不同的事情,此外,如果您指定了一个无效值,它将尝试加载一个模块不存在并且无法加载任何其他内容。
transpiler
的有效值为:"plugin-traceur"
、"plugin-babel"
、"plugin-typescript"
和 false
。我们不仅希望 TypeScript 建议这 4 种可能性,而且还希望它检查我们是否只使用了这些可能性中的一种。
在我们可以使用离散值作为类型之前,这个 API 很难建模。
充其量,我们必须写成类似
的东西
transpiler: string | boolean;
这不是我们想要的,因为只有 3 个有效字符串并且 true
不是有效值!
通过使用值作为类型,我们实际上可以将这个 API 精确地描述为
transpiler: 'plugin-traceur' | 'plugin-babel' | 'plugin-typescript' | false;
并且不仅知道我们可以传递哪些值,而且如果我们错误地键入 'plugin-tsc'
或尝试传递 true
.
,则会立即出现错误
因此文字类型及早捕获错误,同时能够精确描述荒野中现有的 APIs。
另一个好处是控制流分析,它允许编译器检测常见的逻辑错误。这是一个复杂的话题,但这里有一个简单的例子:
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
本质上是死代码,它的主体永远不会被执行。文字类型允许我们将可能的罗盘 direction
声明为 'N'、'S'、'E' 或 'W' 之一的特殊性使编译器能够立即标记第三个 if 语句是无法访问的,实际上是无意义的代码,表明我们的程序中存在错误,逻辑错误(毕竟我们只是人类)。
因此,我们有一个主要的激励因素,可以定义与可能值的非常具体的子集相对应的类型。最后一个例子最好的部分是它全部在我们自己的代码中。我们想声明一个合理但高度具体的合同,语言为我们提供了表达能力,然后在我们违反自己的合同时抓住了我们。
And how Javascript handle those on compile time?
与所有其他 TypeScript 类型完全相同的方式。它们从 TypeScript 编译器发出的 JavaScript 中完全删除。
And How it will differ from readonly and constant?
与所有 TypeScript 类型一样,您所说的类型,即表示特定值的类型,与 const
和 readonly
修饰符交互。这种交互有些复杂,可以像我将在此处做的那样浅层处理,也可以很容易地包含一个 Q/A 本身。
可以这么说,const
和 readonly
对可能的值有影响,因此对变量或 属性 实际可以随时保存的可能类型有影响,因此使文字类型,即特定值类型的类型,更易于传播、推理,也许最重要的是为我们推断。
因此,当某些东西是不可变的时,推断其类型尽可能具体通常是有意义的,因为它的值不会改变。
const x = 'a';
推断 x
的类型为 'a'
因为它不能被重新分配。
let x = 'a';
另一方面,推断 x
的类型为 string
因为它是可变的。
现在你可以写了
let x: 'a' = 'a';
在这种情况下,尽管它是可变的,但它只能被分配一个 'a'
.
类型的值
请注意,出于说明目的,这有点过于简单化了。
在上面的 if else if
示例中可以观察到其他机制在起作用,该示例表明该语言还有另一层,即控制流分析层,它跟踪可能的值类型通过条件、赋值、真值检查和解构赋值等其他结构缩小范围。
现在让我们详细检查您问题中的 class,属性 by 属性:
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;
}
TypeScript 完全是关于类型推断的,推断越多越好,因为它能够传播来自值(例如表达式)的类型信息,并使用它来推断更精确的类型。
在大多数语言中,类型系统以类型开始,但在 TypeScript 中,几乎一直都是这样,类型系统以值开始。所有值都有类型。对这些值的操作产生具有新类型的新值,允许类型干扰进一步传播到程序中。
如果您将普通 JavaScript 程序粘贴到 TypeScript 文件中,您会注意到,在不添加任何类型注释的情况下,它能够了解程序的很多结构。文字类型进一步增强了这种能力。
关于文字类型还有很多可以说的,出于解释的目的,我省略并简化了某些内容,但请放心,它们很棒。
为什么 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
}
- Why does TypeScript allow a value as a data type?
- How does JavaScript handle these at compile time?
- How does it differ from
readonly
andconstant
?
readonly error= 'test';
对比 error: 'test' = 'test';
一个原因是要处理同一变量的多种类型。这就是为什么打字稿允许您为类型使用特定值。
let x: true | false | 'dog';
x = true; // works
x = false; // works
x = 'cat'; // compilation error
在这种情况下 let x: true
只是一种特殊情况,只有一种类型。
似乎扩展了字符串文字类型功能以允许其他类型的值。也许有更好的文档示例,但我只能找到手册中的字符串文字类型部分 here.
Why does TypeScript accept a value as a data type?
这是字符串文字类型的扩展,这个 PR 解释了它:literal types
How does JavaScript handle these at compile time?
它是纯 typescript 创建的,不会影响结果 javascript。
How does it differ from readonly and 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
}
}
首先,一些非正式的背景和信息将有助于我们讨论您提出的问题:
一般来说,一个类型表示一组 0 个或多个值。这些值可以被认为是该类型的成员或居民。
就他们可以取的值的多样性而言,类型往往属于 3 组中的 1 组。
第 1 组: 例证:string
类型。 string
类型包含所有字符串值。由于一个字符串实际上可以是任意长的,所以本质上有无限个数值是 string
类型的成员。作为此类型成员的值集是所有可能字符串的集。
第 2 组: 例证:undefined
类型。 undefined
类型只有一个值,即 undefined
值。因此,这种类型通常被称为单例类型,因为它的成员集只有 1 个值。
第 3 组: 例证:never
类型。 never
类型没有成员。根据定义,不可能有 never
类型的值。当您在 pros 中阅读它时,这似乎有点令人困惑,但一个小代码示例可以解释它。
考虑:
function getValue(): never {
throw Error();
}
在上面的例子中,函数 getValue
有一个 return 类型的 never
因为它 never return 是一个值,它总是抛出。因此,如果我们写
const value = getValue();
value
也将是 never
类型。
现在开始第一个问题:
Why typescript allow value as a data type?
有很多很多原因,但有几个特别引人注目的是
根据传递给它们的值对函数的行为进行建模。想到的一个例子是函数 document.getElementsByTagName
。此函数始终采用 string
类型的值,并且始终 return 是包含 HTMLElement
的 NodeList
。然而,根据传递给它的实际字符串值,它会 return 列表中完全不同类型的东西。这些元素之间唯一的共同点是它们都派生自 HTMLElement
.
现在,让我们考虑一下如何写下这个函数的类型签名。我们的第一次尝试可能类似于
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
这是正确的,但不是特别有用。想象一下,我们想要获取页面上所有 HTMLInput 元素的值,以便我们可以将它们发送到我们的服务器。
我们知道,getElementsByTagName('input')
,实际上return只是页面上的输入元素,正是我们想要的,但是有了我们上面的定义,我们当然得到了正确的值( TypeScript 不会影响 JavaScript 运行时行为),它们将具有错误的类型。具体来说,它们将是 HTMLElement
类型,HTMLInputElement
的超类型,没有我们想要访问的 value
属性。
那我们能做什么呢?我们可以 "cast" 所有 returned 元素到 HTMLInputElement
但这很丑陋,容易出错(我们必须记住所有类型名称以及它们如何映射到它们的标签名称),冗长,有点迟钝,我们知道得更多,我们知道得更多静态。
因此,需要对 tagname
的 值 之间的关系建模,这是 getElementsByTagName
的参数和它的元素类型实际上 returns.
输入字符串文字类型:
字符串字面量类型是一种更细化的字符串类型,它是单例类型,就像undefined
它只有一个值,即字面量字符串。一旦我们有了这种类型,我们就能够 重载 getElementsByTagName
的声明,使其更加精确和有用
declare function getElementsByTagName(tagname: 'input'): NodeList<HTMLInputElement>;
declare function getElementsByTagName(tagname: string): NodeList<HTMLElement>;
我认为这清楚地证明了具有专门的字符串类型的实用性,从一个值派生并且仅由该单个值居住,但还有很多其他原因需要它们,所以我将讨论更多。
在前面的示例中,我会说易用性是主要动机,但请记住,TypeScript 的第一目标是通过编译时、静态分析来捕获编程错误。
鉴于此,另一个动机是精度。有很多很多 JavaScript API 取一个特定的值,根据它是什么,它们可能会做一些非常不同的事情,什么都不做,或者会失败。
所以,对于另一个真实世界的例子,SystemJS 是一个优秀且广泛使用的模块加载器,具有广泛的配置 API。您可以传递给它的选项之一称为 transpiler
,根据您指定的值,会发生非常不同的事情,此外,如果您指定了一个无效值,它将尝试加载一个模块不存在并且无法加载任何其他内容。
transpiler
的有效值为:"plugin-traceur"
、"plugin-babel"
、"plugin-typescript"
和 false
。我们不仅希望 TypeScript 建议这 4 种可能性,而且还希望它检查我们是否只使用了这些可能性中的一种。
在我们可以使用离散值作为类型之前,这个 API 很难建模。
充其量,我们必须写成类似
的东西transpiler: string | boolean;
这不是我们想要的,因为只有 3 个有效字符串并且 true
不是有效值!
通过使用值作为类型,我们实际上可以将这个 API 精确地描述为
transpiler: 'plugin-traceur' | 'plugin-babel' | 'plugin-typescript' | false;
并且不仅知道我们可以传递哪些值,而且如果我们错误地键入 'plugin-tsc'
或尝试传递 true
.
因此文字类型及早捕获错误,同时能够精确描述荒野中现有的 APIs。
另一个好处是控制流分析,它允许编译器检测常见的逻辑错误。这是一个复杂的话题,但这里有一个简单的例子:
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
本质上是死代码,它的主体永远不会被执行。文字类型允许我们将可能的罗盘 direction
声明为 'N'、'S'、'E' 或 'W' 之一的特殊性使编译器能够立即标记第三个 if 语句是无法访问的,实际上是无意义的代码,表明我们的程序中存在错误,逻辑错误(毕竟我们只是人类)。
因此,我们有一个主要的激励因素,可以定义与可能值的非常具体的子集相对应的类型。最后一个例子最好的部分是它全部在我们自己的代码中。我们想声明一个合理但高度具体的合同,语言为我们提供了表达能力,然后在我们违反自己的合同时抓住了我们。
And how Javascript handle those on compile time?
与所有其他 TypeScript 类型完全相同的方式。它们从 TypeScript 编译器发出的 JavaScript 中完全删除。
And How it will differ from readonly and constant?
与所有 TypeScript 类型一样,您所说的类型,即表示特定值的类型,与 const
和 readonly
修饰符交互。这种交互有些复杂,可以像我将在此处做的那样浅层处理,也可以很容易地包含一个 Q/A 本身。
可以这么说,const
和 readonly
对可能的值有影响,因此对变量或 属性 实际可以随时保存的可能类型有影响,因此使文字类型,即特定值类型的类型,更易于传播、推理,也许最重要的是为我们推断。
因此,当某些东西是不可变的时,推断其类型尽可能具体通常是有意义的,因为它的值不会改变。
const x = 'a';
推断 x
的类型为 'a'
因为它不能被重新分配。
let x = 'a';
另一方面,推断 x
的类型为 string
因为它是可变的。
现在你可以写了
let x: 'a' = 'a';
在这种情况下,尽管它是可变的,但它只能被分配一个 'a'
.
请注意,出于说明目的,这有点过于简单化了。
在上面的 if else if
示例中可以观察到其他机制在起作用,该示例表明该语言还有另一层,即控制流分析层,它跟踪可能的值类型通过条件、赋值、真值检查和解构赋值等其他结构缩小范围。
现在让我们详细检查您问题中的 class,属性 by 属性:
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;
}
TypeScript 完全是关于类型推断的,推断越多越好,因为它能够传播来自值(例如表达式)的类型信息,并使用它来推断更精确的类型。
在大多数语言中,类型系统以类型开始,但在 TypeScript 中,几乎一直都是这样,类型系统以值开始。所有值都有类型。对这些值的操作产生具有新类型的新值,允许类型干扰进一步传播到程序中。
如果您将普通 JavaScript 程序粘贴到 TypeScript 文件中,您会注意到,在不添加任何类型注释的情况下,它能够了解程序的很多结构。文字类型进一步增强了这种能力。
关于文字类型还有很多可以说的,出于解释的目的,我省略并简化了某些内容,但请放心,它们很棒。