Cocos小游戏开发-TypeScript
参考:
TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程。TypeScript 的设计目标是开发大型应用,然后转译成 JavaScript 运行。由于 TypeScript 是 JavaScript 的超集,任何现有的 JavaScript 程序都是合法的 TypeScript 程序。
语言特性
TypeScript 是一种给 JavaScript 添加特性的语言扩展。增加的功能包括:
- 类型批注和编译时类型检查
- 类型推断
- 类型擦除
- 接口
- 枚举
- Mixin
- 泛型编程
- 名字空间
- 元组
- Await
以下功能是从 ECMA 2015 反向移植而来:
- 类
- 模块
- lambda 函数的箭头语法
- 可选参数以及默认参数
JavaScript 与 TypeScript 的区别
ypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。
TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。
TypeScript 安装
1 | npm install -g typescript |
安装完成后我们可以使用 tsc 命令来执行 TypeScript 的相关代码,以下是查看版本号:
1 | $ tsc -v |
然后我们新建一个 app.ts 的文件,代码如下:
1 | var message:string = "Hello World" |
通常我们使用 .ts 作为 TypeScript 代码文件的扩展名。
然后执行以下命令将 TypeScript 转换为 JavaScript 代码:
1 | tsc app.ts |
这时候在当前目录下(与 app.ts 同一目录)就会生成一个 app.js 文件,代码如下:
1 | var message = "Hello World"; |
使用 node 命令来执行 app.js 文件:
1 | $ node app.js |
TypeScript 转换为 JavaScript 过程如下图:
我们可以同时编译多个 ts 文件:
1 | tsc file1.ts file2.ts file3.ts |
sc 常用编译参数如下表所示:
编译参数说明 | |
---|---|
–help | 显示帮助信息 |
–module | 载入扩展模块 |
–target | 设置 ECMA 版本 |
–declaration | 额外生成一个 .d.ts 扩展名的文件 |
–removeComments | 删除文件的注释 |
–out | 编译多个文件并合并到一个输出的文件 |
–sourcemap | 生成一个 sourcemap (.map) 文件。sourcemap 是一个存储源代码与编译代码对应位置映射的信息文件。 |
–module noImplicitAny | 在表达式和声明上有隐含的 any 类型时报错 |
–watch | 在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译。 |
TypeScript 语法
TypeScript 基础语法
TypeScript 程序由以下几个部分组成:
- 模块
- 函数
- 变量
- 语句和表达式
- 注释
TypeScript 保留关键字
break | as | catch | switch |
---|---|---|---|
case | if | throw | else |
var | number | string | get |
module | type | instanceof | typeof |
public | private | enum | export |
finally | for | while | void |
null | super | this | new |
in | return | true | false |
any | extends | static | let |
package | implements | interface | function |
do | try | yield | const |
continue |
空白和换行
TypeScript 会忽略程序中出现的空格、制表符和换行符。
空格、制表符通常用来缩进代码,使代码易于阅读和理解。
TypeScript 区分大小写
TypeScript 区分大写和小写字符。
分号是可选的
每行指令都是一段语句,你可以使用分号或不使用, 分号在 TypeScript 中是可选的,建议使用。
以下代码都是合法的:
1 | console.log("Runoob") |
如果语句写在同一行则一定需要使用分号来分隔,否则会报错,如:
1 | console.log("Runoob");console.log("Google"); |
TypeScript 注释
注释是一个良好的习惯,虽然很多程序员讨厌注释,但还是建议你在每段代码写上文字说明。
注释可以提高程序的可读性。
注释可以包含有关程序一些信息,如代码的作者,有关函数的说明等。
编译器会忽略注释。
TypeScript 支持两种类型的注释
- 单行注释 ( // ) − 在 // 后面的文字都是注释内容。
- 多行注释 (/* */) − 这种注释可以跨越多行。
TypeScript 与面向对象
面向对象是一种对现实世界理解和抽象的方法。
TypeScript 是一种面向对象的编程语言。
面向对象主要有两个概念:对象和类。
- 对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
- 类:类是一个模板,它描述一类对象的行为和状态。
- 方法:方法是类的操作的实现步骤。
下图中 girl、boy 为类,而具体的每个人为该类的对象:
TypeScript 面向对象编程实例:
1 | class Site { |
以上实例定义了一个类 Site,该类有一个方法 name(),该方法在终端上输出字符串 Runoob。
new 关键字创建类的对象,该对象调用方法 name()。
编译后生成的 JavaScript 代码如下:
1 | var Site = /** @class */ (function () { |
执行以上 JavaScript 代码,输出结果如下:
1 | Runoob |
TypeScript 基础类型
TypeScript 包含的数据类型如下表:
数据类型 | 关键字 | 描述 | ||
任意类型 | any | 声明为 any 的变量可以赋予任意类型的值。 | ||
数字类型 |
number |
双精度 64 位浮点值。它可以用来表示整数和分数。
|
||
字符串类型 |
string |
一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式。
|
||
布尔类型 |
boolean |
表示逻辑值:true 和 false。
|
||
数组类型 |
无 |
声明变量为数组。
|
||
元组 |
无 |
元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。
|
||
枚举 |
enum |
枚举类型用于定义数值集合。
|
||
void |
void |
用于标识方法返回值的类型,表示该方法没有返回值。
|
||
undefined | undefined | 用于初始化变量为一个未定义的值 | ||
never | never | never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值 |
Any 类型
任意值是 TypeScript 针对编程时类型不明确的变量使用的一种数据类型,它常用于以下三种情况。
- 变量的值会动态改变时,比如来自用户的输入,任意值类型可以让这些变量跳过编译阶段的类型检查,示例代码如下:
1
2
3let x: any = 1; // 数字类型
x = 'I am who I am'; // 字符串类型
x = false; // 布尔类型 - 改写现有代码时,任意值允许在编译时可选择地包含或移除类型检查,示例代码如下:
1
2
3let x: any = 4;
x.ifItExists(); // 正确,ifItExists方法在运行时可能存在,但这里并不会检查
x.toFixed(); // 正确 - 定义存储各种类型数据的数组时,示例代码如下:
1
2let arrayList: any[] = [1, false, 'fine'];
arrayList[1] = 100;
Null 和 Undefined
null
在 JavaScript 中 null 表示 “什么都没有”。
null是一个只有一个值的特殊类型。表示一个空对象引用。
用 typeof 检测 null 返回是 object。
undefined
在 JavaScript 中, undefined 是一个没有设置值的变量。
typeof 一个没有值的变量会返回 undefined。
Null 和 Undefined 是其他任何类型(包括 void)的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 null 或 undefined。而在TypeScript中启用严格的空校验(–strictNullChecks)特性,就可以使得null 和 undefined 只能被赋值给 void 或本身对应的类型,示例代码如下:
1 | // 启用 --strictNullChecks |
上面的例子中变量 x 只能是数字类型。如果一个类型可能出现 null 或 undefined, 可以用 | 来支持多种类型,示例代码如下:
1 | // 启用 --strictNullChecks |
never 类型
never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明为 never 类型的变量只能被 never 类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环),示例代码如下:
1 | let x: never; |
TypeScript 变量声明
变量使用前必须先声明,我们可以使用 var 来声明变量。
我们可以使用以下四种方式来声明变量:
声明变量的类型及初始值:
1 | var [变量名] : [类型] = 值; |
声明变量的类型,但没有初始值,变量值会设置为 undefined:
1 | var [变量名] : [类型]; |
声明变量并初始值,但不设置类型,该变量可以是任意类型:
1 | var [变量名] = 值; |
声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined:
1 | var [变量名]; |
TypeScript 遵循强类型,如果将不同的类型赋值给变量会编译错误,如下实例:
1 | var num:number = "hello" // 这个代码会编译错误 |
类型推断
当类型没有给出时,TypeScript 编译器利用类型推断来推断类型。
如果由于缺乏声明而不能推断出类型,那么它的类型被视作默认的动态 any 类型。
1 | var num = 2; // 类型推断为 number |
- 第一行代码声明了变量 num 并=设置初始值为 2。 注意变量声明没有指定类型。因此,程序使用类型推断来确定变量的数据类型,第一次赋值为 2,num 设置为 number 类型。
- 第三行代码,当我们再次为变量设置字符串类型的值时,这时编译会错误。因为变量已经设置为了 number 类型
1 | error TS2322: Type '"12"' is not assignable to type 'number'. |
变量作用域
变量作用域指定了变量定义的位置。
程序中变量的可用性由变量作用域决定。
TypeScript 有以下几种作用域:
- 全局作用域 − 全局变量定义在程序结构的外部,它可以在你代码的任何位置使用。
- 类作用域 − 这个变量也可以称为 字段。类变量声明在一个类里头,但在类的方法外面。 该变量可以通过类的对象来访问。类变量也可以是静态的,静态的变量可以通过类名直接访问。
- 局部作用域 − 局部变量,局部变量只能在声明它的一个代码块(如:方法)中使用。
以下实例说明了三种作用域的使用:
1 | var global_num = 12 // 全局变量 |
以上代码使用 tsc 命令编译为 JavaScript 代码为:
1 | var global_num = 12; // 全局变量 |
执行以上 JavaScript 代码,输出结果为:
1 | 全局变量为: 12 |
如果我们在方法外部调用局部变量 local_num,会报错:
1 | error TS2322: Could not find symbol 'local_num'. |
TypeScript 运算符
算术运算符
运算符 | 描述 | 例子 |
---|---|---|
+ | 加法 | x=y+2 |
- | 减法 | x=y-2 |
* | 乘法 | x=y*2 |
/ | 除法 | x=y/2 |
% | 取模(余数) | x=y%2 |
++ | 自增 | x=++y(x=y+1) |
++ | 自增 | x=y++(x=y; y=y+1) |
– | 自减 | x=y–; x=–y |
关系运算符
关系运算符用于计算结果是否为 true 或者 false。
运算符 | 描述 |
---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
逻辑运算符
逻辑运算符用于测定变量或值之间的逻辑。
给定 x=6 以及 y=3,下表解释了逻辑运算符:
运算符 | 描述 | 例子 |
---|---|---|
&& | and | (x < 10 && y > 1) 为 true |
|| | or | (x==5 |
! | not | !(x==y) 为 true |
三元运算符 (?)
1 | Test ? expr1 : expr2 |
1 | var num:number = -2 |
类型运算符
typeof 运算符
typeof 是一元运算符,返回操作数的数据类型。
1 | var num = 12 |
使用 tsc 命令编译以上代码得到如下 JavaScript 代码:
1 | var num = 12; |
instanceof
instanceof 运算符用于判断对象是否为指定的类型
字符串运算符: 连接运算符 (+)
- 运算符可以拼接两个字符串,查看以下实例:
1
2var msg:string = "RUNOOB"+".COM"
console.log(msg) // RUNOOB.COM
TypeScript 条件语句
if 语句
1 | var num:number = 5 |
if…else 语句
1 | var num:number = 12; |
if…else if….else 语句
1 | var num:number = 2 |
switch…case 语句
1 | var grade:string = "A"; |
TypeScript 循环
for 循环
1 | var num:number = 5; |
for…in 循环
1 | var j:any; |
输出
1 | a |
for…of 、forEach、every 和 some 循环
此外,TypeScript 还支持 for…of 、forEach、every 和 some 循环。
for…of 语句创建一个循环来迭代可迭代的对象。在 ES6 中引入的 for…of 循环,以替代 for…in 和 forEach() ,并支持新的迭代协议。
for…of 允许你遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构等。
1 | let someArray = [1, "string", false]; |
forEach、every 和 some 是 JavaScript 的循环语法,TypeScript 作为 JavaScript 的语法超集,当然默认也是支持的。
因为 forEach 在 iteration 中是无法返回的,所以可以使用 every 和 some 来取代 forEach。
1 | let list = [4, 5, 6]; |
1 | let list = [4, 5, 6]; |
while 循环
1 | var num:number = 5; |
do…while 循环
不像 for 和 while 循环,它们是在循环头部测试循环条件。do…while 循环是在循环的尾部检查它的条件。
1 | var n:number = 10; |
无限循环
无限循环就是一直在运行不会停止的循环。 for 和 while 循环都可以创建无限循环。
for 创建无限循环语法格式:
1 | for(;;) { |
while 创建无限循环语法格式:
1 | while(true) { |
TypeScript 函数
1 | function function_name() |
调用函数
1 | function_name() |
函数返回值
1 | function function_name():return_type { |
示例:
1 | // 函数定义 |
带参数函数
1 | function add(x: number, y: number): number { |
可选参数和默认参数
在 TypeScript 函数里,如果我们定义了参数,则我们必须传入这些参数,除非将这些参数设置为可选,可选参数使用问号标识 ?。
实例
1 | function buildName(firstName: string, lastName?: string) { |
可选参数必须跟在必需参数后面。 如果上例我们想让 firstName 是可选的,lastName 必选,那么就要调整它们的位置,把 firstName 放在后面。
如果都是可选参数就没关系。
默认参数
参数不能同时设置为可选和默认。
1 | function calculate_discount(price:number,rate:number = 0.50) { |
剩余参数
有一种情况,我们不知道要向函数传入多少个参数,这时候我们就可以使用剩余参数来定义。
剩余参数语法允许我们将一个不确定数量的参数作为一个数组传入。
1 | function addNumbers(...nums:number[]) { |
匿名函数
匿名函数是一个没有函数名的函数。
匿名函数在程序运行时动态声明,除了没有函数名外,其他的与标准函数一样。
我们可以将匿名函数赋值给一个变量,这种表达式就成为函数表达式。
1 | var msg = function() { |
带参数匿名函数:
1 | var res = function(a:number,b:number) { |
匿名函数自调用
匿名函数自调用在函数后使用 () 即可:
1 | (function () { |
构造函数
TypeScript 也支持使用 JavaScript 内置的构造函数 Function() 来定义函数:
语法格式如下:
1 | var res = new Function ([arg1[, arg2[, ...argN]],] functionBody) |
- arg1, arg2, … argN:参数列表。
- functionBody:一个含有包括函数定义的 JavaScript 语句的字符串。
1 | var myFunction = new Function("a", "b", "return a * b"); |
Lambda 函数
Lambda 函数也称之为箭头函数。
箭头函数表达式的语法比函数表达式更短。
函数只有一行语句:
1 | var foo = (x:number)=>10 + x |
编译以上代码,得到以下 JavaScript 代码:
1 | var foo = function (x) { return 10 + x; }; |
函数是一个语句块:
1 | var foo = (x:number)=> { |
我们可以不指定函数的参数类型,通过函数内来推断参数类型:
1 | var func = (x)=> { |
单个参数 ()
是可选的:
1 | var display = x => { |
函数重载
重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
定义函数重载需要定义重载签名和一个实现签名。
1 | function disp(s1:string):void; |
TypeScript Number
1 | var num = new Number(value); |
注意: 如果一个参数值不能转换为一个数字将返回 NaN (非数字值)。
MAX_VALUE
可表示的最大的数,MAX_VALUE 属性值接近于 1.79E+308。大于 MAX_VALUE 的值代表 “Infinity”。
MIN_VALUE
可表示的最小的数,即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE,MIN_VALUE 的值约为 5e-324。小于 MIN_VALUE (“underflow values”) 的值将会转换为 0。
NaN
非数字值(Not-A-Number)。
NEGATIVE_INFINITY
负无穷大,溢出时返回该值。该值小于 MIN_VALUE。
POSITIVE_INFINITY
正无穷大,溢出时返回该值。该值大于 MAX_VALUE。
prototype
Number 对象的静态属性。使您有能力向对象添加属性和方法。
constructor
返回对创建此对象的 Number 函数的引用。
1 | console.log("TypeScript Number 属性: "); |
1 | TypeScript Number 属性: |
NaN 实例
1 | var month = 0 |
prototype 实例
1 | function employee(id:number,name:string) { |
Number 对象方法
方法 & 描述 | 实例 | ||
toExponential() 把对象的值转换为指数计数法。 |
|
||
toFixed() 把数字转换为字符串,并对小数点指定位数。 |
|
||
toLocaleString() 把数字转换为字符串,使用本地数字格式顺序。 |
|
||
toPrecision() 把数字格式化为指定的长度。 |
|
||
toString() 把数字转换为字符串,使用指定的基数。数字的基数是 2 ~ 36 之间的整数。若省略该参数,则使用基数 10。 |
|
||
valueOf() 返回一个 Number 对象的原始数字值。 |
|
TypeScript String(字符串)
1 | var txt = new String("string"); |
String 对象属性
属性 & 描述 | 实例 | ||||
constructor
对创建该对象的函数的引用。 |
输出:
|
||||
length 返回字符串的长度。 |
|
||||
prototype 允许您向对象添加属性和方法。 |
|
String 方法
方法 & 描述 | 实例 | ||
charAt() 返回在指定位置的字符。 |
|
||
charCodeAt() 返回在指定的位置的字符的 Unicode 编码。 |
|
||
concat() 连接两个或更多字符串,并返回新的字符串。 |
|
||
indexOf() 返回某个指定的字符串值在字符串中首次出现的位置。 |
|
||
lastIndexOf() 从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。 |
|
||
localeCompare() 用本地特定的顺序来比较两个字符串。 |
|
||
match() 查找找到一个或多个正则表达式的匹配。 |
|
||
replace() 替换与正则表达式匹配的子串 |
|
||
search() 检索与正则表达式相匹配的值 |
|
||
slice() 提取字符串的片断,并在新的字符串中返回被提取的部分。 |
|
||
substr() 从起始索引号提取字符串中指定数目的字符。 |
|
||
toLocaleLowerCase() 根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射。 |
|
||
toLocaleUpperCase() 据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射。 |
|
||
toLowerCase() 把字符串转换为小写。 |
|
||
toString() 返回字符串。 |
|
||
toUpperCase() 把字符串转换为大写。 |
|
||
valueOf() 返回指定字符串对象的原始值。 |
|
TypeScript Array(数组)
1 | var nums:number[] = [1,2,3,4] |
Array 对象
我们也可以使用 Array 对象创建数组。
1 | var arr_names:number[] = new Array(4) |
以下实例我们直接初始化数组元素:
1 | var sites:string[] = new Array("Google","Runoob","Taobao","Facebook") |
数组解构
我们也可以把数组元素赋值给变量,如下所示:
1 | var arr:number[] = [12,13] |
数组迭代
1 | var j:any; |
多维数组
一个数组的元素可以是另外一个数组,这样就构成了多维数组(Multi-dimensional Array)。
1 | var arr_name:datatype[][]=[ [val1,val2,val3],[v1,v2,v3] ] |
1 | var multi:number[][] = [[1,2,3],[23,24,25]] |
数组在函数中的使用
作为参数传递给函数
1 | var sites:string[] = new Array("Google","Runoob","Taobao","Facebook") |
作为函数的返回值
1 | function disp():string[] { |
数组方法
下表列出了一些常用的数组方法:
方法 & 描述 | 实例 | ||||
concat() 连接两个或更多的数组,并返回结果。 |
|
||||
every() 检测数值元素的每个元素是否都符合条件。 |
|
||||
filter() 检测数值元素,并返回符合条件所有元素的数组。 |
|
||||
forEach() 数组每个元素都执行一次回调函数。 |
编译成 JavaScript 代码:
|
||||
indexOf() 搜索数组中的元素,并返回它所在的位置。 如果搜索不到,返回值 -1,代表没有此项。 |
|
||||
join() 把数组的所有元素放入一个字符串。 |
|
||||
lastIndexOf() 返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。 |
|
||||
map() 通过指定函数处理数组的每个元素,并返回处理后的数组。 |
|
||||
pop() 删除数组的最后一个元素并返回删除的元素。 |
|
||||
push() 向数组的末尾添加一个或更多元素,并返回新的长度。 |
|
||||
reduce() 将数组元素计算为一个值(从左到右)。 |
|
||||
reduceRight() 将数组元素计算为一个值(从右到左)。 |
|
||||
reverse() 反转数组的元素顺序。 |
|
||||
shift() 删除并返回数组的第一个元素。 |
|
||||
slice() 选取数组的的一部分,并返回一个新数组。 |
|
||||
some() 检测数组元素中是否有元素符合指定条件。 |
|
||||
sort() 对数组的元素进行排序。 |
|
||||
splice() 从数组中添加或删除元素。 |
|
||||
toString() 把数组转换为字符串,并返回结果。 |
|
||||
unshift() 向数组的开头添加一个或更多元素,并返回新的长度。 |
|
TypeScript Map 对象
Map 对象保存键值对,并且能够记住键的原始插入顺序。
任何值(对象或者原始值) 都可以作为一个键或一个值。
Map 是 ES6 中引入的一种新的数据结构,可以参考 ES6 Map 与 Set。
创建 Map
1 | let myMap = new Map(); |
1 | let myMap = new Map([ |
Map 相关的函数与属性:
- map.clear() – 移除 Map 对象的所有键/值对 。
- map.set() – 设置键值对,返回该 Map 对象。
- map.get() – 返回键对应的值,如果不存在,则返回 undefined。
- map.has() – 返回一个布尔值,用于判断 Map 中是否包含键对应的值。
- map.delete() – 删除 Map 中的元素,删除成功返回 true,失败返回 false。
- map.size – 返回 Map 对象键/值对的数量。
- map.keys() - 返回一个 Iterator 对象, 包含了 Map 对象中每个元素的键 。
- map.values() – 返回一个新的Iterator对象,包含了Map对象中每个元素的值 。
1 | let nameSiteMapping = new Map(); |
输出
1 | 2 |
迭代 Map
Map 对象中的元素是按顺序插入的,我们可以迭代 Map 对象,每一次迭代返回 [key, value] 数组。
TypeScript使用 for…of 来实现迭代:
1 | let nameSiteMapping = new Map(); |
使用 es6 编译:
1 | tsc --target es6 test.ts |
1 | let nameSiteMapping = new Map(); |
TypeScript 元组
我们知道数组中元素的数据类型都一般是相同的(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组。
1 | var mytuple = [10,"Runoob"]; |
元组运算
- push() 向元组添加元素,添加在最后面。
- pop() 从元组中移除元素(最后一个),并返回移除的元素。
1 | var mytuple = [10,"Hello","World","typeScript"]; |
更新元组
1 | var mytuple = [10, "Runoob", "Taobao", "Google"]; // 创建一个元组 |
解构元组
1 | var a =[10,"Runoob"] |
TypeScript 联合类型
联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。
注意:只能赋值指定的类型,如果赋值其它类型就会报错。
1 | var val:string|number |
也可以将联合类型作为函数参数使用:
1 | function disp(name:string|string[]) { |
联合类型数组
1 | var arr:number[]|string[]; |
TypeScript 接口
接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
1 | interface IPerson { |
需要注意接口不能转换为 JavaScript。 它只是 TypeScript 的一部分。
联合类型和接口
1 | interface RunOptions { |
接口和数组
接口中我们可以将数组的索引值和元素设置为不同类型,索引值可以是数字或字符串。
设置元素为字符串类型:
1 | interface namelist { |
接口继承
接口继承就是说接口可以通过其他接口来扩展自己。
Typescript 允许接口继承多个接口。
继承使用关键字 extends。
单接口继承语法格式:
1 | interface Person { |
多继承实例
1 | interface IParent1 { |
TypeScript 类
TypeScript 是面向对象的 JavaScript。
类描述了所创建的对象共同的属性和方法。
TypeScript 支持面向对象的所有特性,比如 类、接口等。
TypeScript 类定义方式如下:
1 | class class_name { |
定义类的关键字为 class,后面紧跟类名,类可以包含以下几个模块(类的数据成员):
- 字段 − 字段是类里面声明的变量。字段表示对象的有关数据。
- 构造函数 − 类实例化时调用,可以为类的对象分配内存。
- 方法 − 方法为对象要执行的操作。
1 | class Car { |
创建实例化对象
1 | var obj = new Car("Engine 1") |
类的继承
1 | class Shape { |
需要注意的是子类只能继承一个父类,TypeScript 不支持继承多个类,但支持多重继承,如下实例:
1 | class Root { |
继承类的方法重写
类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。
其中 super 关键字是对父类的直接引用,该关键字可以引用父类的属性和方法。
1 | class PrinterClass { |
static 关键字
static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。
1 | class StaticMem { |
instanceof 运算符
instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false。
1 | class Person{ } |
访问控制修饰符
TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。TypeScript 支持 3 种不同的访问权限。
- public(默认) : 公有,可以在任何地方被访问。
- protected : 受保护,可以被其自身以及其子类访问。
- private : 私有,只能被其定义所在的类访问。
以下实例定义了两个变量 str1 和 str2,str1 为 public,str2 为 private,实例化后可以访问 str1,如果要访问 str2 则会编译错误。
1 | class Encapsulate { |
类和接口
类可以实现接口,使用关键字 implements,并将 interest 字段作为类的属性使用。
以下实例中 AgriLoan 类实现了 ILoan 接口:
1 | interface ILoan { |
TypeScript 对象
1 | var sites = { |
此外对象也可以作为一个参数传递给函数,如下实例:
1 | var sites = { |
鸭子类型(Duck Typing)
鸭子类型(英语:duck typing)是动态类型的一种风格,是多态(polymorphism)的一种形式。
在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由”当前方法和属性的集合”决定。
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
在鸭子类型中,关注点在于对象的行为能做什么,而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为”鸭子”的对象,并调用它的”走”和”叫”方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的”走”和”叫”方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的”走”和”叫”方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
1 | interface IPoint { |
TypeScript 命名空间
IShape.ts 文件代码:
1 | namespace Drawing { |
Circle.ts 文件代码:
1 | /// <reference path = "IShape.ts" /> |
Triangle.ts 文件代码:
1 | /// <reference path = "IShape.ts" /> |
TestShape.ts 文件代码:
1 | /// <reference path = "IShape.ts" /> |
使用 tsc 命令编译以上代码:
1 | tsc --out app.js TestShape.ts |
输出
1 | $ node app.js |
嵌套命名空间
命名空间支持嵌套,即你可以将命名空间定义在另外一个命名空间里头。
Invoice.ts 文件代码:
1 | namespace Runoob { |
InvoiceTest.ts 文件代码:
1 | /// <reference path = "Invoice.ts" /> |
使用 tsc 命令编译以上代码:
1 | tsc --out app.js InvoiceTest.ts |
TypeScript 模块
TypeScript 模块的设计理念是可以更换的组织代码。
模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里面的变量、函数和类等在模块外部是不可见的,除非明确地使用 export 导出它们。类似地,我们必须通过 import 导入其他模块导出的变量、函数、类等。
两个模块之间的关系是通过在文件级别上使用 import 和 export 建立的。
模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。 大家最熟知的JavaScript模块加载器是服务于 Node.js 的 CommonJS 和服务于 Web 应用的 Require.js。
此外还有有 SystemJs 和 Webpack。
模块导出使用关键字 export 关键字,语法格式如下:
1 | // 文件名 : SomeInterface.ts |
要在另外一个文件使用该模块就需要使用 import 关键字来导入:
1 | import someInterfaceRef = require("./SomeInterface"); |
实例:
IShape.ts 文件代码:
1 | /// <reference path = "IShape.ts" /> |
Circle.ts 文件代码:
1 | import shape = require("./IShape"); |
Triangle.ts 文件代码:
1 | import shape = require("./IShape"); |
TestShape.ts 文件代码:
1 | import shape = require("./IShape"); |
TypeScript 声明文件
TypeScript 作为 JavaScript 的超集,在开发过程中不可避免要引用其他第三方的 JavaScript 的库。虽然通过直接引用可以调用库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。为了解决这个问题,需要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生了一个描述 JavaScript 库和模块信息的声明文件。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了。
假如我们想使用第三方库,比如 jQuery,我们通常这样获取一个 id 是 foo 的元素:
1 | $('#foo'); |
但是在 TypeScript 中,我们并不知道 $ 或 jQuery 是什么东西
这时,我们需要使用 declare 关键字来定义它的类型,帮助 TypeScript 判断我们传入的参数类型对不对:
1 | declare var jQuery: (selector: string) => any; |
declare 定义的类型只会用于编译时的检查,编译结果中会被删除。
上例的编译结果是:
1 | jQuery('#foo'); |
声明文件
声明文件以 .d.ts
为后缀,例如:
1 | runoob.d.ts |
声明文件或模块的语法格式如下:
1 | declare module Module_Name { |
TypeScript 引入声明文件语法格式:
1 | /// <reference path = " runoob.d.ts" /> |
当然,很多流行的第三方库的声明文件不需要我们定义了,比如 jQuery 已经有人帮我们定义好了:jQuery in DefinitelyTyped。
以下定义一个第三方库来演示:
CalcThirdPartyJsLib.js 文件代码:
1 | var Runoob; |
如果我们想在 TypeScript 中引用上面的代码,则需要设置声明文件 Calc.d.ts,代码如下:
1 | declare module Runoob { |
声明文件不包含实现,它只是类型声明,把声明文件加入到 TypeScript 中:
CalcTest.ts 文件代码:
1 | /// <reference path = "Calc.d.ts" /> |
使用 tsc 命令来编译以上代码文件:
1 | tsc CalcTest.ts |
最后我们编写一个 runoob.html 文件,引入 CalcTest.js 文件及第三方库 CalcThirdPartyJsLib.js:
1 |
|
浏览器打开该文件输出结果如下:
TypeScript 测验
QA 记录
您可以通过启用编译器选项来禁用隐式变量类型赋值,该选项是?
noImplicitAny
TypeScript 始终可以正确推断变量类型?
正确
您可以通过启用哪个编译器属性来启用“未定义 undefined”和“空 null”类型?
strictNullChecks
以下哪个选项类似于 ‘any’,但在不确定类型时更安全。
unknown
‘readonly’ 访问修饰符对数组变量赋值有什么作用?
TypeScript 始终能够正确推断出数组的类型?
错误
类型别名通常与什么类型一起使用?
Strings
接口类似于类型别名,但仅适用于对象类型?
错误
keyof 关键字可以与索引名一起使用来提取索引类型?
正确
不能为泛型分配默认值?
错误
以下代码参数的类型是什么?
1 | function ex(param1?: string){} |
string | undefined
当一个类扩展另一个类,并替换其父类的成员时,它被称为什么?
override (覆盖)