本章描述TypeScript为JavaScript表达式提供类型推断和类型检查的方式。 TypeScript的类型分析完全在编译时进行,并且不增加表达式评估的运行时开销。
TypeScript的键入规则为每个表达式构造定义一个类型。 例如,文字123的类型是Number原语类型,而对象文字{{a:10,b:“ hello”}的类型是{a:number; b:字符串; }。 本章的各节详细介绍了这些规则。
除了类型推断和类型检查之外,TypeScript还使用以下结构来扩展JavaScript表达式:
除非后面各节中另有说明,否则TypeScript表达式和从它们生成的JavaScript表达式是相同的。
表达式被分类为值或引用。 引用是允许作为赋值目标的表达式子集。 具体来说,引用是标识符(第4.3节),括号(第4.8节)和属性访问(第4.13节)的组合。 本章中描述的所有其他表达式构造都归类为值。
表达式中此类型取决于引用发生的位置:
在所有其他上下文中,引用此是编译时错误。
注意,箭头函数(第4.11节)没有此参数,而是保留了其封闭上下文的this。
当表达式是IdentifierReference时,该表达式引用具有该名称的最嵌套的命名空间,类,枚举,函数,变量或参数,其名称范围(第2.4节)包括引用的位置。 此类表达式的类型是与引用实体关联的类型:
引用变量或参数的标识符表达式被分类为引用。 引用任何其他种类的实体的标识符表达式被分类为值(因此不能成为分配的目标)。
文字的类型如下:
对象文字被扩展为支持方法中的类型注释以及获取和设置访问器。
PropertyDefinition :(Modified)
IdentifierReference
CoverInitializedName
PropertyName:AssignmentExpression
PropertyName CallSignature {FunctionBody}
GetAccessor
SetAccessor
GetAccessor:
get PropertyName()TypeAnnotationopt {FunctionBody}
SetAccessor:
set Property(BindingIdentifierOrPattern TypeAnnotationopt){FunctionBody}
对象文字的类型是一种对象类型,其属性集由对象文字中的属性分配指定。 get和set访问器可以指定相同的属性名称,但否则为同一属性指定多个属性分配是错误的。
表格的简写属性分配
prop
相当于
prop : prop
同样
f ( ... ) { ... }
相当于
f : function ( ... ) { ... }
对象文字中的每个属性分配按以下方式处理:
由形式为Name的属性分配引入的属性类型:Expr是Expr的类型。
get访问器声明的处理方式与不带参数的普通函数声明(第6.1节)相同。设置访问者声明的处理方式与具有单个参数和Void返回类型的普通函数声明的处理方式相同。同时为属性声明get和set访问器时:
如果为属性声明了get访问器,则get访问器的返回类型将成为属性的类型。如果仅为属性声明集合访问器,则集合访问器的参数类型(如果不存在类型注释,则可以为Any)成为属性的类型。
当对象文字通过包含字符串索引签名的类型在上下文中键入时,对象文字的结果类型包括字符串索引签名,该字符串索引签名具有在对象文字中声明的属性的类型的并集类型,或者为Undefined类型(如果存在)对象文字为空。同样,当对象文字通过包含数字索引签名的类型在上下文中键入时,对象文字的结果类型包括数字索引签名,该数字索引签名具有声明的数字命名属性的类型的联合类型(第3.9.4节)在对象文字中,如果对象文字未声明任何数字命名的属性,则为Undefined类型。
如果属性分配的PropertyName是不代表众所周知的符号(2.2.3)的计算的属性名称,则该构造被视为动态属性分配。以下规则适用于动态属性分配:
数组文法
[ expr1, expr2, ..., exprN ]
表示取决于上下文的数组类型(第3.3.2节)或元组类型(第3.3.3节)的值。
非空数组文字中的每个元素表达式的处理如下:
数组文字表达式的结果类型确定如下:
扩展元素必须指定类似数组类型的表达式(第3.3.2节),否则会发生错误。
TODO:编译器当前不支持将扩展运算符应用于字符串(将字符串的各个字符扩展为字符串数组)。最终将允许这样做,但前提是代码生成目标是ECMAScript 2015或更高版本。
TODO:将迭代器扩展到数组文字中的文档。
上面的规则意味着数组文字始终是数组类型的,除非它是由类似元组的类型在上下文中键入的。例如
var a = [1, 2]; // number[]
var b = ["hello", true]; // (string | boolean)[]
var c: [number, string] = [3, "three"]; // [number, string]
当输出目标是ECMAScript 3或5时,包含扩展元素的数组文字将重写为concat方法的调用。 例如,作业
var a = [2, 3, 4];
var b = [0, 1, ...a, 5, 6];
可以重写为
var a = [2, 3, 4];
var b = [0, 1].concat(a, [5, 6]);
TODO:模板文法
带括弧的表达式
( expr )
具有与包含的表达式本身相同的类型和分类。 具体而言,如果将包含的表达式分类为参考,则带括号的表达式也是如此。
可以在表达式中使用super关键字来引用基类属性和基类构造函数。
超级调用由关键字super组成,后跟括号中的参数列表。 超级调用仅在派生类的构造函数中允许,如第8.3.2节所述。
超级调用在此引用的实例上调用基类的构造函数。 使用基类构造函数类型的构造签名作为用于过载解析的候选签名的初始集合,将超级调用作为函数调用(第4.15节)进行处理。 类型参数不能在超级调用中明确指定。 如果基类是泛型类,则用于处理超级调用的类型参数始终是在引用基类的extends子句中指定的类型参数。
超级调用表达式的类型为Void。
在8.7.2节中指定了为超级调用生成的JavaScript代码。
超级属性访问由关键字super,后跟点和标识符组成。超级属性访问用于从派生类访问基类成员函数,并且在此内容(第4.2节)引用派生类实例或派生类构造函数的上下文中允许访问。特别:
超级属性访问通常用于访问派生类成员函数中重写的基类成员函数。有关此示例,请参见8.4.2节。
在8.7.2节中指定了为超级属性访问而生成的JavaScript代码。
待办事项:更新部分以在超级属性访问中包括括号符号。
函数表达式从JavaScript扩展到可选地包括参数和返回类型注释。
FunctionExpression :(Modified)
function BindingIdentifieropt CallSignature {FunctionBody}
第6章中提供的函数声明的描述也适用于函数表达式,但函数表达式不支持重载。
函数表达式的类型是一种对象类型,其中包含单个调用签名,并带有从函数表达式的签名和主体推断出的参数和返回类型。
当没有类型参数且没有参数类型注释的函数表达式由类型T进行上下文类型化(第4.23节)并且可以从T提取上下文签名S时,该函数表达式将被视为具有明确指定的参数类型注释,如下所示:它们存在于S中。参数通过位置匹配,并且不需要具有匹配的名称。如果函数表达式的参数少于S,则忽略S中的其他参数。如果函数表达式的参数多于S,则所有附加参数均被视为具有Any类型。
同样,当通过函数类型T在上下文中键入没有返回类型注释的函数表达式(第4.23节),并且可以从T中提取上下文签名S时,包含在返回语句中的表达式(第5.10节)将通过返回类型在上下文中进行键入。的S。
如下从功能类型T中提取上下文签名S:
在这个例子中
var f: (s: string) => string = function (s) {
return s.toLowerCase();
};
函数表达式是根据类型’f’在上下文中键入的,并且由于函数表达式没有类型参数或类型注释,因此将从上下文类型中提取其参数类型信息,从而将’s’的类型推断为String原语 类型。
箭头功能从JavaScript扩展到可选的参数和返回类型注释。
ArrowFormalParameters :(Modified)
CallSignature
第6章中提供的函数声明的描述也适用于箭头函数,但箭头函数不支持重载。
箭头函数的类型与函数表达式相同(第4.10节)。 同样,以与函数表达式相同的方式在上下文中键入arrow函数的参数和arrow函数主体中的return语句。
当通过函数类型T在上下文中键入带有表达式主体且没有返回类型注释的箭头函数(第4.23节),并且可以从T中提取上下文签名S时,通过返回类型S在上下文中键入表达式主体。
形式的箭头函数表达式
( ... ) => expr
完全等同于
( ... ) => { return expr ; }
此外,表单的箭头函数表达式
id => { ... }
id => expr
因此,以下示例都是等效的:
(x) => { return Math.sin(x); }
(x) => Math.sin(x)
x => { return Math.sin(x); }
x => Math.sin(x)
函数表达式引入了一个新的动态绑定对象,而箭头函数表达式保留了其封闭上下文的对象。 箭头函数表达式对于编写回调特别有用,否则回调通常具有未定义或意外的含义。
在这个例子中
class Messenger {
message = "Hello World";
start() {
setTimeout(() => alert(this.message), 3000);
}
};
var messenger = new Messenger();
messenger.start();
使用箭头函数表达式会使回调与周围的“开始”方法具有相同的含义。 将回调写为标准函数表达式,必须手动安排对其周围环境的访问,例如,将其复制到局部变量中:
class Messenger {
message = "Hello World";
start() {
var _this = this;
setTimeout(function() { alert(_this.message); }, 3000);
}
};
var messenger = new Messenger();
messenger.start();
TypeScript编译器应用这种类型的转换将箭头函数表达式重写为标准函数表达式。
形式的构造
< T > ( ... ) => { ... }
可以被解析为带有类型参数的箭头函数表达式,或者被解析为没有类型参数的箭头函数的类型断言。 它被解析为前者,但是可以使用括号来选择后者的含义:
< T > ( ( ... ) => { ... } )
TODO:文档类表达式。
属性访问使用点表示法或括号表示法。 属性访问表达式始终被归类为引用。
表单的点表示法属性访问
object . name
其中对象是表达式,名称是标识符(可能包括保留字),用于访问给定对象上具有给定名称的属性。 点符号属性访问在编译时按以下方式处理:
表格的方括号符号属性访问
object [ index ]
其中对象和索引为表达式,用于通过给定对象上的索引表达式计算的名称访问属性。括号表示法属性访问在编译时按以下方式处理:
TODO:符号。
上面的规则意味着,使用带有名称名称表示形式的方括号表示法访问属性时,将强类型化属性。例如:
var type = {
name: "boolean",
primitive: true
};
var s = type["name"]; // string
var b = type["primitive"]; // boolean
元组类型为每个元素分配数字名称,因此,当使用带有数字文字的方括号表示法访问时,将强烈键入元素:
var data: [string, number] = ["five", 5];
var s = data[0]; // string
var n = data[1]; // number
new操作具有以下格式之一:
new C
new C ( ... )
new C < ... > ( ... )
其中C是一个表达式。 第一种形式等效于提供一个空的参数列表。 C必须是Any类型或具有一个或多个构造或调用签名的对象类型。 该操作在编译时按以下方式处理:
从JavaScript扩展了函数调用,以支持可选的类型参数。
Arguments: ( Modified )
TypeArgumentsopt ( ArgumentListopt )
函数调用采用以下形式之一:
func ( ... )
func < ... > ( ... )
其中func是函数类型或Any类型的表达式。 函数表达式后跟一个可选的类型参数列表(第3.6.2节)和一个参数列表。
如果func是Any类型,或者是没有调用或构造签名但是Function接口的子类型的对象类型,则该调用是未类型化的函数调用。 在无类型的函数调用中,不允许使用任何类型的参数,参数表达式可以是任何类型和数字,没有为参数表达式提供上下文类型,并且结果始终是Any类型。
如果func具有明显的调用签名(第3.11.1节),则该调用为类型化函数调用。 TypeScript在类型化函数调用中采用重载解析,以支持具有多个调用签名的函数。 此外,TypeScript可以执行类型参数推断以自动确定通用函数调用中的类型参数。
函数调用中的重载解析的目的是确保至少一个签名适用,为参数提供上下文类型,并确定函数调用的结果类型,这在多个适用的签名之间可能有所不同。重载解析对函数调用的运行时行为没有影响。由于JavaScript不支持函数重载,因此在运行时重要的是函数的名称。
TODO:描述重载解析中通配符函数类型的使用。
类型化函数调用的编译时处理包括以下步骤:
在以下情况下,签名被称为相对于参数列表的适用签名:
TODO:函数调用中的传播运算符和迭代器。
给定签名
TODO:更新类型参数推断和重载解析规则。
类型参数推论为每个类型参数产生一组候选类型。给定类型参数T和一组候选类型,实际推断出的类型参数确定如下:
为了计算候选类型,参数列表按以下方式处理:
通过类型T推论表达式e的过程与通过类型T推论上下文e的过程相同,但以下情况除外:
一个例子:
function choose<T>(x: T, y: T): T {
return Math.random() < 0.5 ? x : y;
}
var x = choose(10, 20); // Ok, x of type number
var y = choose("Five", 5); // Error
在第一次调用“ choose”时,从“ number”到“ T”有两个推论,每个参数一个。 因此,推断“ T”为“ number”,并且该调用等效于
var x = choose<number>(10, 20);
在对“ choose”的第二次调用中,对第一个参数从类型“ string”到“ T”进行推断,对第二个参数从类型“ number”到“ T”进行推断。 由于“字符串”和“数字”都不是另一个的超类型,因此类型推断将失败。 这又意味着没有适用的签名,并且函数调用是错误。
在这个例子中
function map<T, U>(a: T[], f: (x: T) => U): U[] {
var result: U[] = [];
for (var i = 0; i < a.length; i++) result.push(f(a[i]));
return result;
}
var names = ["Peter", "Paul", "Mary"];
var lengths = map(names, s => s.length);
对“ map”的调用中对“ T”和“ U”的推论如下:对于第一个参数,推论从类型“ string []”(类型“ names”)到类型“ T” []”,推断“ T”的“字符串”。 对于第二个参数,箭头表达式’s => s.length’的推论输入会导致’T’变得固定,从而推断出的类型’string’可以用于参数’s’。 然后可以确定箭头表达式的返回类型,并从类型’(s:字符串)=>数字’到类型’(x:T)=> U’进行推断,从而推断’U’的’数字’ '。 因此,对“map”的调用等同于
var lengths = map<string, number>(names, s => s.length);
因此,“长度”的结果类型为“数字[]”。
在这个例子中
function zip<S, T, U>(x: S[], y: T[], combine: (x: S) => (y: T) => U): U[] {
var len = Math.max(x.length, y.length);
var result: U[] = [];
for (var i = 0; i < len; i++) result.push(combine(x[i])(y[i]));
return result;
}
var names = ["Peter", "Paul", "Mary"];
var ages = [7, 9, 12];
var pairs = zip(names, ages, s => n => ({ name: s, age: n }));
在对“ zip”的调用中对“ S”,“ T”和“ U”的推论如下:使用前两个参数,对“ S”的“ string”和对“ T”的“ number”进行推论。 。 对于第三个参数,外箭头表达式的推论类型会导致“ S”变得固定,从而可以将推论类型“ string”用于参数“ s”。 当函数表达式被推论类型时,其返回表达式也被推论类型。 因此,推论性地键入了内部箭头函数,从而使“ T”变得固定,从而可以将推论类型“ number”用作参数“ n”。 然后可以确定内部箭头函数的返回类型,这又确定了从外部箭头函数返回的函数的返回类型,并根据类型’(s:string)=>(n:number)进行推断。 => {名称:string; age:number}‘转换为类型’(x:S)=>(y:T)=> R’,从而推断出’{{name:string; 年龄:数字}‘代表’R’。 因此,对“ zip”的调用等同于
var pairs = zip<string, number, { name: string; age: number }>(
names, ages, s => n => ({ name: s, age: n }));
因此,“ pairs”的结果类型为“ {{name:string; 年龄:数字} []’。
在Arguments产生中包含类型实参(第4.15节)会引起表达式语法的某些歧义。 例如,语句
f(g<A, B>(7));
可以解释为对带有两个参数“ g (7)”的“ f”的调用。 或者,可以将其解释为使用一个参数调用“ f”,这是使用两个类型参数和一个常规参数调用通用函数“ g”。
语法歧义的解决方法如下:在一个可能的标记序列解释为Arguments产生的上下文中,如果标记的初始序列形成语法上正确的TypeArguments产生并且后跟一个’('标记,则该序列 令牌的序列将被处理为Arguments产生,而其他可能的解释将被丢弃,否则,令牌序列将不被视为Arguments产生。
此规则意味着对上面的“ f”的调用被解释为具有一个参数的调用,这是对具有两个类型参数和一个常规参数的通用函数“ g”的调用。 但是,这些陈述
f(g < A, B > 7);
f(g < A, B > +(7));
都被解释为带有两个参数的对“ f”的调用。
TypeScript扩展了JavaScript表达式语法,使其能够声明表达式的类型:
UnaryExpression: ( Modified )
…
< Type > UnaryExpression
类型断言表达式由用<和>括起来的类型以及后跟一元表达式组成。 类型断言表达式纯粹是编译时构造。 类型断言不会在运行时检查,并且不会对生成的JavaScript产生任何影响(因此不会产生运行时成本)。 类型和封闭的<和>只需从生成的代码中删除。
在 e形式的类型断言表达式中,e由T在上下文中键入(第4.23节),并且* e *的结果类型必须可分配给T,或者T必须可分配给扩展形式 结果类型e的取值,否则发生编译时错误。 结果的类型为T。
类型断言在两个方向上检查分配兼容性。 因此,类型断言允许进行可能正确但未知的正确类型转换。 在这个例子中
class Shape { ... }
class Circle extends Shape { ... }
function createShape(kind: string): Shape {
if (kind === "circle") return new Circle();
...
}
var circle = <Circle> createShape("circle");
类型注释指示’createShape’函数可能返回’Circle’(因为’Circle’是’Shape’的子类型),但不知道这样做(因为其返回类型为’Shape’)。 因此,需要类型断言来将结果视为“圆”。
如上所述,在运行时不检查类型断言,并且程序员有责任防止错误,例如使用instanceof运算符:
var shape = createShape(shapeKind);
if (shape instanceof Circle) {
var circle = <Circle> shape;
...
}
TODO:操作算子。
TODO:JSX表达式。
接下来的小节指定一元运算符的编译时处理规则。 通常,如果一元运算符的操作数不满足上述要求,则会发生编译时错误,并且在进一步处理中,运算结果默认为类型Any。
这些运算符以前缀或后缀形式要求其操作数的类型为Any,Number原语类型或枚举类型,并分类为引用(第4.1节)。 它们产生Number原语类型的结果。
这些运算符允许其操作数为任何类型,并产生Number原语类型的结果。
一元+运算符可以方便地用于将任何类型的值转换为Number原语类型:
function getValue() { ... }
var n = +getValue();
上面的示例将“ getValue()”的结果转换为数字(如果还不是数字)。 不管’getValue’的返回类型如何,推断为’n’的类型都是Number原语类型。
! 运算符允许其操作数为任何类型,并产生布尔基元类型的结果。
两个一元! 顺序操作符可以方便地用于将任何类型的值转换为Boolean基本类型:
function getValue() { ... }
var b = !!getValue();
上面的示例将’getValue()'的结果转换为布尔值(如果还不是布尔值的话)。 不论’getValue’的返回类型如何,为’b’推断的类型都是布尔类型的原始类型。
delete运算符采用任何类型的操作数,并产生布尔基元类型的结果。
“ void”运算符采用任何类型的操作数,并产生值“ undefined”。 结果的类型为未定义类型(3.2.7)。
“ typeof”运算符采用任何类型的操作数,并产生String基本类型的值。 在需要类型的位置,也可以在类型查询中使用“ typeof”(第3.8.10节)以生成表达式的类型。
var x = 5;
var y = typeof x; // Use in an expression
var z: typeof x; // Use in a type query
在上面的示例中,“ x”的类型为“数字”,“ y”的类型为“字符串”,因为在表达式中使用时,“ typeof”会生成字符串类型的值(在这种情况下为字符串“ number”) ,而’z’是’number’类型的,因为在类型查询中使用’typeof’可以获取表达式的类型。
接下来的小节指定了二进制运算符的编译时处理规则。 通常,如果二进制运算符的操作数不满足规定的要求,则会发生编译时错误,并且该操作的结果默认为在进一步处理中键入any。 提供了一些表格,这些表格总结了Any类型,Boolean,Number和String原语类型以及所有其他类型(表中的Other列)的操作数的编译时处理规则。
这些运算符要求其操作数的类型为Any,Number基本类型或枚举类型。 枚举类型的操作数被视为具有基本类型Number。 如果一个操作数为空值或未定义值,则将其视为具有另一种操作数的类型。 结果始终是Number原语类型。
Any | Boolean | Number | String | Other | |
---|---|---|---|---|---|
Any | Number | Number | |||
Boolean | |||||
Number | Number | Number | |||
String | |||||
Ohter |
TODO:指数表达式。
二进制+运算符要求两个操作数都为Number原语类型或枚举类型,或至少一个操作数为Any类型或String原语类型。 枚举类型的操作数被视为具有基本类型Number。 如果一个操作数为空值或未定义值,则将其视为具有另一种操作数的类型。 如果两个操作数均为Number原语类型,则结果为Number原语类型。 如果一个或两个操作数均为String基本类型,则结果为String基本类型。 否则,结果的类型为Any。
Any | Boolean | Number | String | Other | |
---|---|---|---|---|---|
Any | Any | Any | Any | String | Any |
Boolean | Any | String | |||
Number | Any | Number | String | ||
String | String | String | String | String | String |
Ohter | Any | String |
通过添加空字符串,可以将任何类型的值转换为String基本类型:
function getValue() { ... }
var s = getValue() + "";
上面的示例将’getValue()'的结果转换为字符串(如果还不是字符串的话)。 不管返回类型为“ getValue”,为“ s”推断的类型都是String原语类型。
这些运算符要求将一种或两种操作数类型分配给另一种。 结果始终是布尔基元类型。
Any | Boolean | Number | String | Other | |
---|---|---|---|---|---|
Any | Boolean | Boolean | Boolean | Boolean | Boolean |
Boolean | Boolean | Boolean | |||
Number | Boolean | Boolean | |||
String | Boolean | Boolean | |||
Ohter | Boolean | Boolean |
instanceof运算符要求左侧操作数的类型为Any,对象类型或类型参数类型,而右侧操作数的类型为Any类型或“函数”接口类型的子类型。 结果始终是布尔基元类型。
请注意,包含一个或多个调用或构造签名的对象类型将自动成为“函数”接口类型的子类型,如3.3节所述。
in运算符要求左操作数的类型为Any,String原语类型或Number原语类型,而右操作数的类型为Any,对象类型或类型参数类型。 结果始终是布尔基元类型。
&&运算符允许操作数为任何类型,并产生与第二个操作数相同类型的结果。
Any | Boolean | Number | String | Other | |
---|---|---|---|---|---|
Any | Any | Boolean | Number | String | Other |
Boolean | Any | Boolean | Number | String | Other |
Number | Any | Boolean | Number | String | Other |
String | Any | Boolean | Number | String | Other |
Ohter | Any | Boolean | Number | String | Other |
|| 运算符允许操作数为任何类型。
如果|| expression是上下文类型的(第4.23节),操作数也使用相同类型的上下文类型。 否则,将不会在上下文中键入左操作数,而在上下文中将根据左操作数的类型来键入右操作数。
结果的类型是两种操作数类型的并集类型。
Any | Boolean | Number | String | Other | |
---|---|---|---|---|---|
Any | Any | Any | Any | Any | Any |
Boolean | Any | Boolean | N | B | S |
Number | Any | N | B | Number | S |
String | Any | S | B | S | N |
Ohter | Any | B | O | N | O |
在形式的条件表达式中
test ? expr1 : expr2
测试表达式可以是任何类型。
如果条件表达式是上下文类型的(第4.23节),则expr1和expr2会使用相同类型的上下文类型。 否则,不会在上下文中键入expr1和expr2。
结果的类型是expr1和expr2类型的并集类型。
赋值的形式
v = expr
要求将v分类为参考(第4.1节)或分配模式(第4.21.1节)。 根据v的类型在上下文中键入expr表达式(第4.23节),并且必须将expr的类型分配给v的类型(第3.11.4节),否则会发生编译时错误。 结果是具有expr类型的值。
形式的复合赋值
v ??= expr
其中?? =是复合赋值运算符之一
*= /= %= += -= <<= >>= >>>= &= ^= |=
与相应的非复合运算符具有相同的要求,并产生相同类型的值。 复合分配还需要将v分类为参考(第4.1节),并且非复合运算的类型可以分配给v的类型。请注意,在复合分配中,不允许v是分配模式。
解构分配是一种赋值操作,其中左侧操作数是由ECMAScript 2015规范中的AssignmentPattern生产定义的解构分配模式。
在解构分配表达式中,右侧的表达式类型必须可分配给左侧的分配目标。如果满足以下条件之一,则认为类型S的表达式可分配给分配目标V:
TODO:当赋值元素E是rest元素时更新以指定行为。
在包含默认值的分配属性或元素中,默认值的类型必须可分配给分配属性或元素中给定的目标。
当输出目标是ECMAScript 2015或更高版本时,解构变量分配在生成的JavaScript代码中保持不变。当输出目标是ECMAScript 3或5时,解构变量分配将重写为一系列简单分配。例如,销毁分配
var x = 1;
var y = 2;
[x, y] = [y, x];
被重写为简单的变量分配
var x = 1;
var y = 2;
_a = [y, x], x = _a[0], y = _a[1];
var _a;
逗号运算符允许操作数为任何类型,并产生与第二个操作数相同类型的结果。
通过将表达式计算出的值的目标类型考虑在内,可以在多种情况下改进表达式的类型检查。在这种情况下,可以说表达式是根据目的地的类型在上下文中键入的。在以下情况下,将在上下文中键入表达式:
在下面的例子中
interface EventObject {
x: number;
y: number;
}
interface EventHandlers {
mousedown?: (event: EventObject) => void;
mouseup?: (event: EventObject) => void;
mousemove?: (event: EventObject) => void;
}
function setEventHandlers(handlers: EventHandlers) { ... }
setEventHandlers({
mousedown: e => { startTracking(e.x, e.y); },
mouseup: e => { endTracking(); }
});
传递给“ setEventHandlers”的对象文字在上下文中键入为“ EventHandlers”类型。 这导致这两个属性分配在上下文中被键入为未命名的函数类型’(event:EventObject)=> void’,这又导致箭头函数表达式中的’e’参数被自动键入为’EventObject’。
类型保护是涉及“ typeof”和“ instanceof”运算符的特定表达模式,这些运算符会使变量或参数的类型缩小为更具体的类型。 例如,在下面的代码中,了解’x’的静态类型以及’typeof’检查的组合,可以很安全地将’x’的类型缩小为’if’语句和数字的第一分支中的字符串 在“ if”语句的第二个分支中。
function foo(x: number | string) {
if (typeof x === "string") {
return x.length; // x has type string here
}
else {
return x + 1; // x has type number here
}
}
在以下情况下,变量或参数的类型变窄:
类型保护只是一个遵循特定模式的表达式。当类型为true或false时,通过类型保护将变量x的类型缩小的过程取决于类型保护,如下所示:
在上述规则中,当缩小操作将所有类型从联合类型中删除时,该操作对联合类型没有影响。
请注意,类型防护仅影响变量和参数的类型,而对诸如属性之类的对象成员没有影响。另请注意,可以通过调用更改受保护变量类型的函数来取消类型保护。
待办事项:用户定义的类型保护功能。
在这个例子中
function isLongString(obj: any) {
return typeof obj === "string" && obj.length > 100;
}
obj参数在&&运算符的右侧操作数中具有字符串类型。
在这个例子中
function processValue(value: number | (() => number)) {
var x = typeof value !== "number" ? value() : value;
// Process number in x
}
value参数在第一个条件表达式中具有type()=> number,在第二个条件表达式中具有type number,并且x的推断类型为number。
在这个例子中
function f(x: string | number | boolean) {
if (typeof x === "string" || typeof x === "number") {
var y = x; // Type of y is string | number
}
else {
var z = x; // Type of z is boolean
}
}
x的类型是字符串| 编号 ||的左操作数中的布尔值 运算符,数字| ||的右操作数中的布尔值 运算符,字符串| if语句的第一分支中的数字,而if语句的第二分支中的布尔值。
在这个例子中
class C {
data: string | string[];
getData() {
var data = this.data;
return typeof data === "string" ? data : data.join(" ");
}
}
数据变量的类型在第一个条件表达式中为string,在第二个条件表达式中为string [],而getData的推断类型为string。 请注意,必须将data属性复制到本地变量,以使类型保护生效。
在这个例子中
class NamedItem {
name: string;
}
function getName(obj: Object) {
return obj instanceof NamedItem ? obj.name : "unknown";
}
在第一个条件表达式中,obj的类型缩小为NamedItem,并且getName函数的推断类型为字符串。