引言:TypeScript是一个开源的、跨平台且带有类型系统的JavaScript超集,它可以编译为纯JavaScript,然后运行在任意的浏览器和其他环境中。它使开发者可以使用一些未来JavaScript标准(ECMAScript 6和7)中的特性。TypeScript为JavaScript添加了可选的静态类型、类和模块,让大型JavaScript应用可以使用更好的工具并拥有更清晰的结构。
本文选自《Learning TypeScript中文版》一书,本文将带您了解类型、变量、基本类型和运算符的语言特性。
1、类型
TypeScript 是 JavaScript 的超集。TypeScript 通过向 JavaScript 增加可选的静态类型声明来把JavaScript变成强类型的程序语言。可选的静态类型声明可约束函数、变量、属性等程序实体,这样编译器和相应的开发工具就可以在开发过程中提供更好的正确性验证和帮助功能(比如IntelliSense)。
强类型特性能让程序员对自己和其他开发团队人员在代码中表达他的意图。
TypeScript的类型检测在编译期进行并且没有运行时开销。
可选的静态类型声明
TypeScript非常擅长类型推导,但是在很多情况下没有办法自动侦测出一个对象或者变量的类型。在这些情况下,TypeScript允许我们明确地声明一个变量的类型。这种允许声明变量类型的功能就是被大家所熟知的可选的静态类型声明(optional static type notation)。对于变量来说,类型声明在变量名后面并且前面有一个冒号:
var counter; // 未知(any)类型
var counter = 0; // number类型(推断出的)
var counter : number; // number类型
var counter : number = 0; // number类型
可以看到,变量的类型声明在变量名后面这种风格是基于类型理论,且更强调类型是可选的。当没有类型声明的时候,TypeScript会尝试检查赋值给变量的值来推测变量的类型。举例来说,在上面代码片段的第二行里,我们可以看到变量counter被识别出是number类型,因为它被赋值为number类型的0。这种类型被自动推测出来的过程被称为类型推导(type inference),当一个变量的类型无法被推测时,一个特殊的类型 any 会作为它的类型。
2、变量、基本类型和运算符
基本类型有boolean、number、string、array、void和所有用户自定义的enum 类型。所有这些类型在TypeScript中,都是一个唯一的顶层的Any Type类型的子类型,any关键字代表这种类型。让我们看一下这些原始类型。
boolean
与string和number数据类型在理论上可以有无数个值不同,boolean类型只可能是两种值。它们分别是true和false。一个 boolean 值是一个真实性的值,它代表条件是否正确。
var isDone: boolean = false;
number
和在JavaScript中一样,所有的数字在TypeScript中都是浮点数。这些浮点数全部都是number类型。
var height: number = 6;
string
在 TypeScript 中,string类型用来表示文本。在代码中使用字符串是将它们放在引号或者双引号中间。单引号中的字符串可以包含双引号,双引号中的字符串也可以包含单引号。
var name: string = "bob";
name = 'smith';
array
和 JavaScript 一样, TypeScript 允许使用数组。array类型的声明有两种写法。第一种,可以在数组元素的类型后面跟着[]来表示包含这种类型元素的数组:
var list:number[] = [1, 2, 3];
第二种是使用范型数组类型Array:
var list:Array<number> = [1, 2, 3];
enum
enum类型是为了给一个数字集合更友好地命名。enum类型中的成员默认从0开始,但你也可以手动设置成员中的值来更改这种默认行为。
enum Color {Red, Green, Blue};
var c: Color = Color.Green;
any
any类型可以表示任意JavaScript值。一个any类型的值支持所有在JavaScript中对它的操作,并且对一个 any 类型的值操作时仅进行最小化静态检查。
var notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; //合法行为定义为一个布尔类型
使用any类型是与现存的JavaScript代码一起工作的一种高效的方式,你可以在编译期逐步添加或者去除类型检查。any类型在你只知道一部分类型的情况下也很方便,比如你可能有一个混合了各种类型元素的数组:
var list:any[] = [1, true, "free"];
list[1] = 100;
void
在某种程度上,any的对立面就是void,即所有的类型都不存在的时候。你会在一个函数没有返回值时看到它:
function warnUser(): void {
alert("This is my warning message");
}
JavaScript的原始类型也包括了undefined和null。在JavaScript中,undefined是全局作用域的一个属性,它会赋值给那些被声明但未被初始化的变量。null是一个字面量(不是全局对象的一个属性),它可以被赋值给那些表示没有值的变量。
var TestVar; // 变量声名但未初化
alert(TestVar); // 显示undefined
alert(typeof TestVar); // 显示undefined
var TestVar = null; // 变量被声名,并且被赋值为null
assigned as value
alert(TestVar); // 显示null
alert(typeof TestVar); // 显示objcect
在 TypeScript 中,我们不能把 null 或 undefined 当作类型使用:
var TestVar : null; // 错误,类型错误
var TestVar : undefined;// 错误,找不到undefined
因为 null 和 undefined 都不能被当作类型使用,所以上面这些代码都是不合法的。
var、let和const
在 TypeScript 中,当声明一个变量时,可以使用var、let和const关键字:
var mynum : number = 1;
let isValid : boolean = true;
const apiKey : string = "0E5CE8BD-6341-4CC2-904D-C4A94ACD276E";
使用var声明的变量保存在最近的函数作用域中(如果不在任何函数中则在全局作用域中)。
使用let声明的变量保存在最近的比函数作用域小的块作用域中(如果不在任何块中则在全局作用域中)。
const关键字会创建一个保存在创建位置作用域中的常量,可以是全局作用域也可以是块作用域。
联合类型
TypeScript 允许声明联合类型:
var path : string[]|string;
path = '/temp/log.xml';
path = ['/temp/log.xml', '/temp/errors.xml'];
path = 1; // 错误
联合类型用来声明那些可以存储多种类型值的变量。在上面这个例子中,我们声明了一个可以存储一个(字符串)或者一组路径(字符串数组)的变量path。在例子中,我们也对这个变量进行了赋值,将字符串和字符串的数组合法地赋值给了这个变量。然而,当试图将一个数字赋值给它时我们遇到了一个编译错误,因为这个联合类型并没有声明 number为它的合法类型。
类型守护
可以在运行时使用typeof或者instanceof运算符对类型进行验证。TypeScript语言服务会在if区域寻找这些运算符,然后对应地更改类型:
var x: any = { /* ... */ };
if(typeof x === 'string') {
console.log(x.splice(3, 1)); // 错误,'string'上不存在'splice'方法
}
// x 依然是 any类型
x.foo(); // 合法
在这段代码中,我们首先声明了一个any类型的变量x,随后在运行时通过typeof运算符对x进行了类型检查。如果x的类型为string时,我们就会尝试调用被认为是x的一个成员的splice方法。TypeScript语言服务可以读懂在条件语句中使用typeof的用法。TypeScript会自动推断出x一定是string类型,然后告诉我们splice方法不存于string类型上。这种特性被称为类型守护。
类型别名
TypeScript允许使用type关键字声明类型别名:
type PrimitiveArray = Array<string|number|boolean>;
type MyNumber = number;
type NgScope = ng.IScope;
type Callback = () => void;
类型别名实质上与原来的类型一样,它们仅仅是一个替代的名字。类型别名可以让代码的可读性更高,但是它也会导致一些问题。
如果你在一个很大的团队中工作,毫无约束地创建类型别名会导致可维护性的问题。在Maintainable JavaScript(由Nicholas C. Zaka所著)一书中,作者建议应避免修改一个不属于你的对象。Nicholas说的是,避免对那些不是你自己声明的对象(DOM对象、BOM对象、原始类型和第三方库)进行修改和覆盖,我们同样能将其应用到别名的使用上。
环境声明
环境声明允许在TypeScript 代码中创建一个不会被编译到 JavaScript中的变量。这个特性是用来促进与现有 JavaScript 代码、DOM(文档对象模型),还有BOM(浏览器对象模型)结合而设计的。让我们看一个例子:
customConsole.log("A log entry!"); // 错误
如果你尝试调用customConsole对象上的log方法,TypeScript会告诉我们customConsole对象未被声明:
// Cannot find name 'customConsole'
出现这种情况并不令人意外。但是,有时候我们希望调用一个未被定义的对象上的方法,比如window对象上的console方法。
console.log("Log Entry!");
var host = window.location.hostname;
当访问 DOM 或 BOM 对象时,我们没有遇到错误,是因为这些对象已经在一个特殊的 TypeScript 文件(被称为声明文件)中被声明了。可以使用declare操作符创建一个环境声明。
在下面这段代码中,我们会声明一个被customConsole对象实现的接口。然后使用declare操作符在作用域中增加一个customConsole对象:
interface ICustomConsole {
log(arg : string) : void;
}
declare var customConsole : ICustomConsole;
然后就可以在没有编译错误的情况下使用customConsole:
customConsole.log("A log entry!"); // 成功
TypeScript 默认包含一个名为lib.d.ts的文件,它提供了像 DOM 这种 JavaScript 内置库的接口声明。
使用.d.ts结尾的声明文件,是用来提高 TypeScript 对第三方库和像 Node.js 或浏览器这种运行时环境的兼容性的。
算术运算符
下表中列出的是TypeScript 支持的算术运算符。为了便于理解下面的例子,设置变量A的值总是10,变量B的值总是20。
比较运算符
下面列出的是 TypeScript 支持的比较运算符。为了便于理解下面的例子,设置变量A的值总是10,变量B的值总是20。
运算符:==
描述:比较两个运算元是否相等,如果相等则结果为 true
例子:(A == B)为 false,A == '10' 为 true
运算符:===
描述:比较两个运算元的值和类型是否都相等,如果都相等则结果为 true
例子:(A === B)为 false,A === '10' 为 false
运算符:!=
描述:比较两个运算元是否不等,如果不等则结果为 true
例子:(A != B) 为 true
运算符:!==
描述:比较两个运算元的类型和值是否都不等,如果都不等则结果为 true
例子:(A !== '10') 为 true
运算符:>
描述:比较左边的运算元是否大于右边的运算元,如果大于则为 true
例子:(A > B) 为 false
运算符:<
描述:比较左边的运算元是否小于右边的运算元,如果小于则为 true
例子:(A < B) 为 true
运算符:>=
描述:比较左边的运算元是否大于或等于右边的运算元,如果大于或者等于则为true
例子:(A >= B) 为 false
运算符:<=
描述:比较左边的运算元是否小于或等于右边的运算元,如果小于或者等于则为true
例子:(A <= B) 为 false
逻辑运算符
下面列出的是TypeScript支持的逻辑运算符。为了便于理解下面的例子,设置变量A的值总是10,变量B的值总是20。
运算符:&&
描述:称为逻辑与操作符,如果两个运算元都为非零,则结果为 true
例子:(A && B)为true
运算符:|
描述:称为逻辑或操作,如果两个运算元任意一个为非零,则结果为true
例子:(A | | B)为true
运算符:!
描述:称为逻辑非操作,用来对运算元取反。如果一个条件是true,那么逻辑非操作会让它变为 false
例子:!(A && B)为false
位运算符
下面列出的是TypeScript支持的位运算符。为了便于理解下面的例子,设置变量A的值总是2,变量B的值总是3。
运算符:&
描述:称为按位与操作符。对两个运算元的每一个二进制位进行逻辑与操作
例子:(A & B) 为 2
运算符:|
描述:称为按位或操作符。对两个运算元的每一个二进制位进行逻辑或操作
例子:(A | B) 为3
运算符:^
描述:称为按位异或操作符。对两个运算元的每一个二进制位进行异或操作。异或操作的意思是两个运算元不同时为 true,相同则为 false
例子:(A ^ B) 为 1
运算符:~
描述:称为按位取反操作符。这是一个一元操作符,它对操作元的每一个二进制位取反
例子:(~B) 为 -4
运算符:<<
描述:称为左移位操作符。将第一个操作元的二进制形式向左移第二个操作元个比特位,右边用0填充。移一位相当于乘以2,移两位相当于乘以4,依此类推
例子:(A << 1) 为 4
运算符:>>
描述:称为有符号右移位操作符。将第一个操作元的二进制形式向右移第二个操作元个比特位,左边用符号位填充
例子:(A >> 1) 为 1
运算符:>>>
描述:称为无符号右移操作符。与有符号右移位类似,除了左边一律使用0 补位
例子:(A >>> 1) 为 1
让我们像C++、Java或者C#那样使用移位操作的一个主要原因是它非常快。但是通常认为,位操作在TypeScript和JavaScript中并没有那么高效。位操作在 JavaScript 中效率不如其他语言的原因是,它必须先将操作元从浮点型(JavaScript 存储数字的数据类型)转换成32位整型进行运算,然后再转换回浮点型。
赋值操作符
下面列出的是TypeScript 支持的赋值操作符。
运算符: 描述 例子
运算符:=
描述:这是最简单的等于操作符,将右边操作元的值赋给左边的操作元
例子:C = A + B 会将 A + B 的结果赋值给 C
运算符:+=
描述:这是加等于操作符,它将右边的操作元与左边的操作元相加后赋值给左边的操作元
例子:C += A 等价于 C = A + C
运算符:- =
描述:这是减等于操作符,它将左边的操作元减去右边的操作元再赋值给左边的操作元
例子:C - = A 等价于 C = C - A
运算符:*=
描述:这是乘等于操作符,它将右边的操作元乘以左边的操作元后赋值给左边的操作元
例子:C = A 等价于 C = C A
运算符:/=
描述:这是除等于操作符,它将左边的操作元除以右边的操作元后赋值给左边的操作元
例子:C /= A 等价于 C = C / A
运算符:%=
描述:这是余等于操作符,它将两个操作元取余后赋值给左边的操作元
例子:C %= A 等价于 C = C % A
想及时获得更多精彩文章,可在微信中搜索“博文视点”或者扫描下方二维码并关注。