基础类型
布尔值
最基本的数据类型就是简单的true/false值,在JavaScript和TypeScript里叫做boolean
(其它语言中也一样)。
let isDone: boolean =false;
数字
number
。 除了支持十进制和十六进制字面量,Typescript还支持ECMAScript 2015中引入的二进制和八进制字面量。
let decLiteral: number = 6; //十进制
let hexLiteral: number = 0xf00d; //十六进制
let binaryLiteral: number = 0b1010; //二进制
let octalLiteral: number = 0o744; //八进制
字符串
JavaScript程序的另一项基本操作是处理网页或服务器端的文本数据。像其它语言里一样,我们使用string
表示文本数据类型。和JavaScript一样,可以使用双引号("
)或单引号('
)表示字符串。
let name: string = "bob";
name = "smith";
let sentence: string = "Hello, my name is " + name + ".\n\n" + "I'll be " + (age + 1) + " years old next month.";
数组
TypeScript像JavaScript一样可以操作数组元素。有两种方式可以定义数组。第一种,可以在元素类型后面接上[]
,表示由此类型元素组成的一个数组:
let list: number[]= [1, 2, 3];
第二种方式是使用数组泛型,Array<
元素类型
>
:
let list: Array<number> = [1, 2, 3];
元组 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。比如,你可以定义一对值分别为string
和number
类型的元组。
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10];// OK
// Initialize it incorrectly
x = [10, 'hello'];// Error
当访问一个已知索引的元素,会得到正确的类型:
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
枚举
enum
类型是对JavaScript标准数据类型的一个补充。像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
默认情况下,从0
开始为元素编号。你也可以手动的指定成员的数值。例如,我们将上面的例子改成从1
开始编号:
enum Color {Red = 1, Green, Blue};
let c: Color = Color.Green;
或者,全部都采用手动赋值:
enum Color {Red = 1, Green = 2, Blue = 4};
let c: Color = Color.Green;
枚举类型提供的一个便利是你可以由枚举的值得到它的名字。例如,我们知道数值为2,但是不确定它映射到Color里的哪个名字,我们可以查找相应的名字:
enum Color {Red = 1, Green, Blue};
let colorName: string =Color[2];
alert(colorName);
任意值
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用any
类型来标记这些变量:
let notSure: any =4;
notSure = "maybea string instead";
notSure = false; // okay, definitely a boolean
在对现有代码进行改写的时候,any
类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。 你可能认为Object
有相似的作用,就像它在其它语言中那样。 但是Object
类型的变量只是允许你给它赋任意值 -- 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:
let notSure: any =4;
notSure.ifItExists(); // okay, ifItExists might existat runtime
notSure.toFixed(); // okay, toFixed exists (butthe compiler doesn't check)
let prettySure: Object =4;
prettySure.toFixed(); // Error: Property 'toFixed'doesn't exist on type 'Object'.
当你只知道一部分数据的类型时,any
类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:
let list: any[]= [1, true, "free"];
list[1] = 100;
空值
某种程度上来说,void
类型像是与any
类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是void
:
functionwarnUser(): void{
alert("This is my warning message");
}
声明一个void
类型的变量没有什么大用,因为你只能为它赋予undefined
和null
:
let unusable: void = undefined;
Null 和 Undefined
TypeScript里,undefined
和null
两者各自有自己的类型分别叫做undefined
和null
。 和void
相似,它们的本身的类型用处不是很大:
// Not much else we can assignto these variables!
let u: undefined =undefined;
let n: null =null;
默认情况下null
和undefined
是所有类型的子类型。 就是说你可以把null
和undefined
赋值给number
类型的变量。
然而,当你指定了--strictNullChecks
标记,null
和undefined
只能赋值给void
和它们各自。 这能避免很多常见的问题。 也许在某处你想传入一个string
或null
或undefined
,你可以使用联合类型string | null | undefined
。 再次说明,稍后我们会介绍联合类型。
注意:我们鼓励尽可能地使用--strictNullChecks
,但在本手册里我们假设这个标记是关闭的
类型断言
有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式。 其一是“尖括号”语法:
let someValue: any ="this is a string";
let strLength: number =(<string>someValue).length;
//另一个为as语法:
let someValue: any ="this is a string";let strLength: number =(someValue as string).length;
两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有as
语法断言是被允许的。
关于let
你可能已经注意到了,我们使用let
关键字来代替大家所熟悉的JavaScript关键字var
。 let
关键字是JavaScript的一个新概念,TypeScript实现了它。 很多常见的问题都可以通过使用let
来解决,所以尽可能地使用let
来代替var
吧,至于为什么大家可以去网上搜索以下,都有很详细的解释。
变量声明
var声明
functionf(shouldInitialize: boolean) {
if (shouldInitialize) {
var x = 10;
}
return x;
}
f(true); //returns '10'
f(false); // returns 'undefined'
有些读者可能要多看几遍这个例子。 变量x
是定义在if
语句里面,但是我们却可以在语句的外面访问它。 这是因为var
声明可以在包含它的函数,模块,命名空间或全局作用域内部任何位置被访问(我们后面会详细介绍),包含它的代码块对此没有什么影响。 有些人称此为var
作用域或函数作用域。 函数参数也使用函数作用域。
这些作用域规则可能会引发一些错误。 其中之一就是,多次声明同一个变量并不会报错:
functionsumMatrix(matrix:number[][]) {
var sum = 0;
for (var i = 0; i < matrix.length; i++) {
var currentRow = matrix[i];
for (var i = 0; i < currentRow.length; i++){
sum +=currentRow[i];
}
}
return sum;
}
这里很容易看出一些问题,里层的for
循环会覆盖变量i
,因为所有i
都引用相同的函数作用域内的变量。 有经验的开发者们很清楚,这些问题可能在代码审查时漏掉,引发无穷的麻烦。
Let声明
当用let
声明一个变量,它使用的是词法作用域或块作用域。不同于使用var
声明的变量那样可以在包含它们的函数外访问,块作用域变量在包含它们的块或for
循环之外是不能访问的。
let hello = "Hello!";
主要的区别不在语法上,而是语义,我们接下来会深入研究。
let声明是块作用域
当用let
声明一个变量,它使用的是词法作用域或块作用域。 不同于使用 var
声明的变量那样可以在包含它们的函数外访问,块作用域变量在包含它们的块或for
循环之外是不能访问的。
function f(input:boolean) {
let a = 100;
if (input) {
// Still okay to reference 'a'
let b = a + 1;
return b;
}
// Error: 'b'doesn't exist here
return b;
}
这里我们定义了2个变量a
和b
。 a
的作用域是f
函数体内,而b
的作用域是if
语句块里。
在catch
语句里声明的变量也具有同样的作用域规则。
try {
throw "oh no!";
}
catch (e) {
console.log("Oh well.");
}
// Error: 'e' doesn't exist here
console.log(e);
拥有块级作用域的变量的另一个特点是,它们不能在被声明之前读或写。 虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于 时间死区。 它只是用来说明我们不能在 let
语句之前访问它们,幸运的是TypeScript可以告诉我们这些信息。
a++; // illegal to use 'a' before it's declared;
let a;
const
声明
const
声明是声明变量的另一种方式。
它们与let
声明相似,但是就像它的名字所表达的,它们被赋值后不能再改变。 换句话说,它们拥有与let
相同的作用域规则,但是不能对它们重新赋值。
这很好理解,它们引用的值是不可变的。
const numLivesForCat = 9;