基础
安装
npm install -g typescript
tsc常用编译参数
保留字
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
变量声明
var 声明
let 声明
const 声明
区分大小写
声明规则:
- 变量声明必须以字母、下划线或者美元符号开头
- 变量声明可以包含数字
- 变量声明不能包含空格
注释:
- 单行注释://
- 多行注释:/* */
Typescript基础类型
Typescript包含的数据类型如下表
上方枚举类型的代码块为什么输出结果是2的解释
这段代码定义了一个枚举类型 Color,其中包含三个枚举成员 Red、Green 和 Blue。默认情况下,枚举成员的值从 0 开始自动递增,所以 Red 的值为 0,Green 的值为 1,Blue 的值为 2。
在代码的第三行,使用 let c: Color = Color.Blue; 将变量 c 声明为 Color 类型,并将其赋值为 Color.Blue。由于 Color.Blue 的值是 2,所以变量 c 的值也是 2。
最后一行的 console.log(c); 将变量 c 的值输出到控制台,因此输出结果为 2。
TypeScript 变量声明
变量是一种使用方便的占位符,用于饮用计算机内存地址。
可以把变量看作存储数据的容器。
TypeScript 变量的命名规范:
- 变量名称可以包含数字字母。
- 除了下划线 _ 和美元 $ 符号外,不能包含其他特殊字符,包括空格。
- 变量名称不能以数字开头。
变量使用前必须先声明,我们可以用var来声明变量。
TypeScript 变量声明语法如下:
- var [变量名] : [类型] = 值; // 声明变量类型和值
- var [变量名] : [类型]; // 声明变量类型
- var [变量名] = 值; // 声明变量为任意类型并且赋值
- var [变量名]; // 声明变量为任意类型
类型断言
类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型。
语法:
<类型>值
或
值 as 类型
var str = '1';
var str2:number = <number> <any> str; // str、str2都是字符串类型
console.log(str2);
这段代码中,首先声明了一个变量 `str` 并将其赋值为字符串 `'1'`,因此 `str` 的类型被推断为字符串类型(string)。
接下来,在第二行使用了类型断言(Type Assertion)的语法,将 `str` 转换为 `number` 类型,并将转换结果赋值给变量 `str2`。类型断言可以通过使用 `<Type>` 或者 `as Type` 的语法来实现。
在这个例子中,使用了两次类型断言:`<number>` 和 `<any>`。第一个断言 `<number>` 将 `str` 断言为 `number` 类型,但实际上 `str` 是一个字符串,这种类型断言是不安全的,因为它只是告诉编译器将 `str` 视为 `number` 类型,但并没有进行实际的类型转换。
接着,使用第二个断言 `<any>`,将 `str` 断言为 `any` 类型,即任意类型。`any` 类型是 TypeScript 中的顶级类型,可以接受任何类型的值。这样做的目的是为了避免编译器报错,因为在 `<number>` 断言之后,编译器会认为 `str` 是一个 `number` 类型,但实际上它仍然是一个字符串。
最后一行的 `console.log(str2)` 将变量 `str2` 的值输出到控制台。由于 `str2` 实际上是一个字符串类型的值,尽管进行了类型断言,输出结果将是字符串 `'1'`。
总结:这段代码中的类型断言并没有进行实际的类型转换,只是告诉编译器将变量视为某种类型,因此输出结果仍然是字符串类型。
TypeScript是怎么确定单个断言是否足够
当S类型是T类型的子集,或者T类型是S类型的的子集时,S能成功被断言成T。这是为了在进行类型断言时提供额外的安全性,完全毫无根据的断言是危险的,如果你想这么做,你可以使用any类型。
它之所以不被成为类型转换,是因为转换通常意味着某种运行时的支持。但是,类型断言纯粹是一个编译时的语法,同时,他也是一种为编译器提供关于如何分析代码的方法。编译后以上代码会生成如下JavaScript代码:
var str = '1';
var str2 = str; // str、str2都是字符串类型
console.log(str2);
执行输出结果为
1
应用场景
-
类型断言:类型断言可以用于将一个类型断言为另一个类型。这在你有充分的理由认为将某个类型断言为特定类型比让它保持原有的类型更有用的时候。例如,将一个父类对象断言为子类对象类型,或者将一个更具体的类型断言为更抽象的类型。
let animal: Animal = new Cat(); let cat = <Cat>animal; // 将animal断言为Cat类型 // 或者 // let cat = animal as Cat;
-
处理第三方库或模块:当你使用第三方库或者模块是,他们可能不具有完整的类型定义或者的类型定义不准确。在这种情况下,你可以使用类型断言来告诉TypeScript你知道值的类型,并且可以继续便携类型安全的代码。
let result: any = someThirdpartyLibrary(); let formattedResult: string = result as string; // 将result断言为string类型
-
解决类型推断问题:有时TypeScript无法正确的推断出变量的类型,但你明确知道它的类型。这时,你可以使用类型断言来指定变量的类型,以便获得更精确的类型检查。
let values = [1, 2, 3, 4, 5]; let firstValue = <number> values[0]; // 将firstValue断言为number类型,或者说是指定 firstValue 的类型为 number
-
处理联合类型;当变量的类型是联合类型时,你可以用类型断言将其断言为其中一个具体的类型,一边在后续的代码中针对特定类型进行操作。
let value: string | number = getValue(); // 假设知道 value 是字符串类型 let length: number = (<string>value).length; // 将value断言为string类型
类型推断
当类型没有给出时,TypeScript编译器利用类型推断来推断类型。
如果由于缺乏生命而不能推断出类型,那么他的类型被视作默认的动态any类型。
let num = 1; // 类型推断为number类型
num = '123'; // 编译错误
console.log(num);
- 第一行代码声明了变量num并且设置初始值为2.注意声明变量并没有指定类型。因此,程序使用类型推断来确定变量的数据类型,第一次赋值为2,num设置为number类型。
- 第二行代码,当我们再次给num赋值为字符串时,这时编译会报错。因为变量已经设置为了number类型。
error TS2322: Type '"123"' is not assignable to type 'number'.
变量作用域
变量作用域指定了变量定义的位置。
程序中变量的可用性由变量作用域决定。
TypeScript由一下几种作用域:
- 全局作用域 - 全局变量定义在程序结构的外部,它可以在代码的任何位置使用。
- 类作用域 - 类变量定义在类的内部,它可以在类的所有方法中使用。这个变量也可以称为字段。类变量声明在一个类里头,但在类方法的外面。该变量可以通过类的对象来访问。类变量也可以是静态的,静态的变量看恶意通过类名直接访问。
- 局部作用域 - 局部变量,局部变量只能在声明他的一个代码块中使用。
运算符
TypeScript主要包含一下几种运算:
- 算术运算符
- 逻辑运算符
- 关系运算符
- 按位运算符
- 赋值运算符
- 三元/条件运算符
- 字符串运算符
- 类型运算符
算术运算符
关系运算符
逻辑运算符
位运算符
赋值运算符
三元/条件运算符
三元运算符由三个数据组成,分别是判断表达式,成立数据表达式,不成立数据表达式。该运算符的主要目的是决定哪个值应该赋值给变量。
TEST ? EXPRESSION1 : EXPRESSION2
TEST - 表达式计算为true或false。
EXPRESSION1 - 如果TEST为true,则返回该值。
EXPRESSION2 - 如果TEST为false,则返回该值。
类型运算符
typeof 运算符
typeof是一元运算符,返回操作数的数据类型。
instanceof 运算符
instanceof是二元运算符,返回一个布尔值,指出对象是否为某个特定类型的实例。
其他运算符
负号运算符(-)
更改操作数的符号。
字符串运算符:连接运算符(+)
+ 运算符用于连接两个或多个字符串。
条件语句
if语句
TypeScript if 语句由一个布尔表达式后跟一个或多个语句组成。
语法
if(布尔表达式)
{
// 如果布尔表达式为 true 则执行该语句块
}
if…else语句
一个 if 语句后可跟一个可选的 else 语句,else 语句在布尔表达式为 false 时执行。
语法
if (布尔表达式) {
// 如果布尔表达式为 true 则执行该语句块
} else {
// 如果布尔表达式为 false 则执行该语句块
}
if…else if…else语句
一个 if 语句后可跟一个可选的 else if…else 语句,这种语句是多个 if…else 语句的扩展。
语法
if(布尔表达式 1){
// 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){
// 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){
// 如果布尔表达式 3 为 true 则执行该语句块
}else {
// 如果以上布尔表达式都不为 true 则执行该语句块
}
switch…case语句
switch 语句必须遵循下面的规则:
- switch 语句中的 expression 是一个要被比较的表达式,可以是任何类型,包括基本数据类型(如 number、string、boolean)、对象类型(如 object、Array、Map)以及自定义类型(如 class、interface、enum)等。
- 在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。
- case 的 constant-expression 必须与 switch 中的变量 expression 具有相同或兼容的数据类型。
- 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
- 当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
- 不是每一个 case 都需要包含 break。如果 case 语句不包含 break,控制流将会 继续 后续的 case,直到遇到 break 为止。
- 一个 switch 语句可以有一个可选的 default case,出现在 switch 的结尾。default 关键字则表示当表达式的值与所有 case 值都不匹配时执行的代码块。default case 中的 break 语句不是必需的。
switch(expression){
case constant-expression :
statement(s);
break; /* 可选的 */
case constant-expression :
statement(s);
break; /* 可选的 */
/* 您可以有任意数量的 case 语句 */
default : /* 可选的 */
statement(s);
}
循环语句
for循环
var num:number = 5;
var i:number;
var factorial = 1;
for(i = num;i>=1;i--) {
factorial *= i;
}
console.log(factorial)
for…in循环
for…in 语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)。
var j:any;
var n:any = "a b c"
for(j in n) {
console.log(n[j])
}
j 需要为string或者any类型,否则会报错
for…of循环、forEach循环、every循环、some循环
for…of 语句创建一个循环来迭代可迭代的对象。在ES6中引入的新特性,它可以替代 for…in 和 forEach() ,并且支持新的迭代协议。for…of 允许你遍历 Arrays(数组)、Strings(字符串)、Maps(映射)、Sets(集合)等可迭代的数据结构。
let someArray = [1, "string", false];
for (let entry of someArray) {
console.log(entry); // 1, "string", false
}
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。forEach() 对于空数组是不会执行回调函数的。
因为 forEach 在 iteration 中是无法返回的,所以可以使用 every 和 some 来取代 forEach。
let list = [4, 5, 6];
list.forEach((val, idx, array) => {
// val: 当前值
// idx:当前index
// array: Array
});
every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
let list = [4, 5, 6];
list.every((val, idx, array) => {
// val: 当前值
// idx:当前index
// array: Array
return true/false; // 循环中断条件
});
while循环
while 循环会在指定条件为 true 时循环执行代码块。
while(condition)
{
// code block to be executed
}
do…while循环
do…while 循环与 while 循环相似,但是 do…while 循环会确保至少执行一次循环。
do
{
// code block to be executed
}while(condition);
break语句
break 语句用于跳出循环/终止循环。
- break 语句可以跳出一个循环。
- break 语句用于跳出 switch 语句。
for (var i = 0; i < 10; i++) {
if (i === 3) { break; }
console.log(i);
}
continue语句
continue 跳出此次循环执行下一次循环。
函数
函数的定义
function add(x: number, y: number): number {
return x + y;
}
函数返回值类型为number,参数类型为number。
可选参数
function add(x: number, y?: number): number {
if (y) {
return x + y;
} else {
return x;
}
}
可选参数必须跟在必须参数后面。
默认参数
function add(x: number, y: number = 0): number {
return x + y;
}
剩余参数
function add(x: number, ...restOfNumber: number[]): number {
let result = x;
for (let i = 0; i < restOfNumber.length; i++) {
result += restOfNumber[i];
}
return result;
}
函数重载
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {
return x + y;
}
在 TypeScript 中,函数重载(Function Overloading)允许我们为同一个函数提供多个不同的函数签名。这样,根据传入的参数类型或参数个数的不同,编译器可以选择正确的函数定义进行调用。
在给定的示例中,我们定义了一个名为 add 的函数,并使用不同的函数签名进行了重载。这个函数可以接受两个参数,根据参数的类型进行不同的操作,并返回相应的结果。
第一个函数签名是 add(x: number, y: number): number,表示当传入两个数字类型的参数时,函数返回一个数字类型的结果。
第二个函数签名是 add(x: string, y: string): string,表示当传入两个字符串类型的参数时,函数返回一个字符串类型的结果。
最后,我们提供了一个通用的函数定义 add(x: any, y: any): any,它可以接受任意类型的参数,并返回一个任意类型的结果。在这个例子中,我们简单地将参数进行连接并返回。
使用函数重载可以提高代码的可读性和类型安全性,因为编译器可以根据函数的重载定义来进行类型检查和推断。这样,在调用函数时,编译器会根据传入的参数类型选择合适的函数定义,避免了潜在的类型错误。
箭头函数
let add = (x: number, y: number): number => x + y;
匿名函数
let add = function (x: number, y: number): number {
return x + y;
}
构造函数
我们使用构造函数形式创建了一个新的函数 add,这个函数接受两个参数 x 和 y,并返回它们的和。
构造函数 Function 是 JavaScript/TypeScript 中的内置对象,它允许我们动态创建一个新的函数。它的参数是函数的参数列表和函数体的字符串表示。
在这个例子中,我们使用了如下的构造函数语法:
new Function('x', 'y', 'return x + y');
其中 ‘x’ 和 ‘y’ 是函数的参数名,‘return x + y’ 是函数体表示两个参数相加并返回结果。
然后,我们调用了这个新创建的函数 add,传入参数 1 和 2,并将返回值赋给变量 result。接着使用 console.log 打印出 result 的值,结果为 3,即参数 1 和 2 相加的结果。
尽管使用构造函数形式创建函数可以实现动态创建函数的目的,但通常情况下,我们更常用的是函数声明或函数表达式的方式来定义函数,因为这种方式更直观、易读,并且在编译时会进行更好的类型检查和优化。构造函数方式创建函数一般用于特殊需求或动态执行代码的场景。
let add = new Function('x', 'y', 'return x + y');
let result = add(1, 2);
console.log(result); // 3
递归函数
function factorial(n: number): number {
if (n === 1) {
return 1; // 递归终止条件
} else {
return n * factorial(n - 1); // 递归调用
}
}
lambda表达式
let add = (x: number, y: number): number => x + y;
Lambda 函数也称之为箭头函数。
箭头函数表达式的语法比函数表达式更短。
Number
TS中的number类型和JS中的number类型是一样的,都是浮点数,没有整型。
都支持Number对象的所有属性和方法。
var num = new Number(value);
Number对象属性
Number对象方法
String
String对象用于处理文本(字符串)。
String对象属性
String对象方法
Array
数组对象是使用单独的变量名来存储一系列的值,并通过数字索引来访问这些值。
var arr_name:datatype[]; // 声明数组
var arr_name:datatype[] = new Array(size); // 声明并初始化数组
var arr_name:datatype[] = [val1,val2,valn..] // 声明并初始化数组
多维数组
var arr_name:datatype[][] = [ [val1,val2,val3],[v1,v2,v3] ]
数组在函数中的使用
function disp(arr:number[]) {
for(var i = 0;i < arr.length;i++) {
console.log(arr[i]);
}
}
作为函数的返回值
function disp():number[] {
return [1,2,3,4,5];
}
元组
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。比如,你可以定义一对值分别为string和number类型的元组。
var tuple_name = [value1,value2,value3,…value n]
元组运算
- push() 向元组添加元素,添加在最后面
- pop() 从元组中移除元素,移除最后一个元素
更新元组
var mytuple = [10,"Hello","World","typeScript"];
console.log("元组的第一个元素为:" + mytuple[0]);
mytuple[0] = 121;
console.log("元组中的第一个元素更新为:" + mytuple[0]);
解构元组
var a = [10,"Hello"];
var [b,c] = a;
console.log(b);
console.log(c);
联合类型
联合类型可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。
注:智能赋值指定的类型,如果赋值其他类型就会报错。
Type1|Type2|Type3
var val:string|number
val = 12
console.log("数字为:"+val)
val = "String"
console.log("字符串为:"+val)
联合类型数组
var arr:number[]|string[];
var i:number;
arr = [1,2,4]
console.log("**数字数组**")
for(i = 0;i < arr.length;i++) {
console.log(arr[i])
}
arr = ["Google","Taobao"]
console.log("**字符串数组**")
for(i = 0;i < arr.length;i++) {
console.log(arr[i])
}
接口
接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的去实现,然后第三方就可以通过这些方法调用,而不用考虑具体的实现。接口是一种规范,通过接口可以实现多态性。
接口的定义:
interface interface_name {
// 接口内容
}
实例
interface IPerson {
firstName:string,
lastName:string,
sayHi: ()=>string
}
var customer:IPerson = {
firstName:"Tom",
lastName:"Hanks",
sayHi: ():string =>{return "Hi there"}
}
console.log("Customer 对象 ")
console.log(customer.firstName)
console.log(customer.lastName)
console.log(customer.sayHi())
var employee:IPerson = {
firstName:"Jim",
lastName:"Blakes",
sayHi: ():string =>{return "Hello!!!"}
}
console.log("Employee 对象 ")
console.log(employee.firstName);
console.log(employee.lastName);
联合类型和接口
interface RunOptions {
program:string;
commandline:string[]|string|(()=>string);
}
// commandline 是字符串
var options:RunOptions = {program:"test1",commandline:"Hello"};
// commandline 是字符串数组
options = {program:"test1",commandline:["Hello","World"]};
// commandline 是一个函数表达式
options = {program:"test1",commandline:()=>{return "**Hello World**";}};
接口和数组
interface namelist {
[index:number]:string
}
// var list2:namelist = ["John",1,"Bran"] // 错误元素 1 不是 string 类型
interface ages {
[index:string]:number
}
var agelist:ages;
agelist["John"] = 15 // 正确
agelist[2] = "nine" // 错误
接口继承
接口继承就是说接口可以通过其他接口来扩展自己。
Typescript 允许接口继承多个接口。
继承使用关键字 extends。
单接口继承语法格式:
Child_interface_name extends super_interface_name
多接口继承语法
Child_interface_name extends super_interface1_name, super_interface2_name,…,super_interfaceN_name
继承的各个接口使用逗号 , 分隔。
实例
interface Person {
age:number
}
interface Musician extends Person {
instrument:string
}
var drummer = <Musician>{};
drummer.age = 27
drummer.instrument = "Drums"
console.log("年龄: "+drummer.age)
console.log("喜欢的乐器: "+drummer.instrument)
多继承实例
interface IParent1 {
v1:number
}
interface IParent2 {
v2:number
}
interface Child extends IParent1, IParent2 { }
var Iobj:Child = { v1:12, v2:23}
console.log("value 1: "+Iobj.v1+" value 2: "+Iobj.v2)
类
TypeScript 是面向对象的JavaScript
类描述了所创建对象的共同的属性和方法。
TypeScript 支持面向对象的所有特性,比如 类、接口等。
TypeScript 类定义方式如下:
class class_name {
// 类作用域
}
定义类的关键字为class,后面紧跟雷鸣,类可以包含以下几个模块(类的数据成员):
- 字段 − 字段是类里面声明的变量。字段表示对象的有关数据。
- 构造函数 − 类实例化时调用,可以为类的对象分配内存。
- 方法 − 方法为对象要执行的操作。
创建类的数据成员
class Car {
// 字段
engine:string;
// 构造函数
constructor(engine:string) {
this.engine = engine
}
// 方法
disp():void {
console.log("发动机为 : "+this.engine)
}
}
创建实例化对象
var object_name = new class_name([ arguments ])
类中的字段属性和方法可以使用 . 号访问。
// 访问属性
obj.field_name;
// 访问方法
obj.function_name()
类的继承
TypeScript 支持继承类,即一个类可以从另一个类中继承属性和方法。
class child_class_name extends parent_class_name
class Shape {
Area:number
constructor(a:number) {
this.Area = a
}
}
class Circle extends Shape {
disp():void {
console.log("圆的面积: "+this.Area)
}
}
var obj = new Circle(223);
obj.disp()
需要注意的是子类只能继承一个父类,TypeScript 不支持继承多个类,但支持多重继承,如下实例:
class Root {
str:string;
}
class Child extends Root {}
class Leaf extends Child {} // 多重继承,继承了 Child 和 Root 类
var obj = new Leaf();
obj.str ="hello"
console.log(obj.str)
继承类的方法重写
类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。
其中 super 关键字是对父类的直接引用,该关键字可以引用父类的属性和方法。
class PrinterClass {
doPrint():void {
console.log("父类的 doPrint() 方法。")
}
}
class StringPrinter extends PrinterClass {
doPrint():void {
super.doPrint() // 调用父类的函数
console.log("子类的 doPrint()方法。")
}
}
static 关键字
TypeScript 类定义加上 static 关键字,则方法就是静态方法。静态方法在调用时不需要实例化类,直接通过类名来调用。
class StaticMem {
static num:number;
static disp():void {
console.log("num 值为 "+ StaticMem.num)
}
}
StaticMem.num = 12 // 初始化静态变量
StaticMem.disp() // 调用静态方法
instanceof 运算符
instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false。
class Person{}
var obj = new Person()
var isPerson = obj instanceof Person;
console.log("obj 对象是 Person 类实例化来的吗? " + isPerson);
访问控制修饰符
TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。
- public(默认) : 公有,可以在任何地方被访问。
- protected : 受保护,可以被其自身以及其子类和父类访问。
- private : 私有,只能被其定义所在的类访问。
class Encapsulate {
str1:string = "hello"
private str2:string = "world"
}
var obj = new Encapsulate()
console.log(obj.str1) // 可访问
console.log(obj.str2) // 编译错误, str2 是私有的
类和接口
类可以实现接口,使用关键字 implements,并将 interest 字段作为类的属性使用。
interface ILoan {
interest:number
}
class AgriLoan implements ILoan {
interest:number
rebate:number
constructor(interest:number,rebate:number) {
this.interest = interest
this.rebate = rebate
}
}
var obj = new AgriLoan(10,1)
console.log("利润为 : "+obj.interest+",抽成为 : "+obj.rebate )
TypeScript对象
对象是包含一组键值对的实例。值可以是标量、函数、数组、对象等。
let obj: object = {
name: 'Jack', // 标量
age: 18,
key1: function () {
// 函数
},
key2: ['a', 'b', 'c'] // 集合(数组或元组)
};
TypeScript类型模板
类型模板是一种特殊的对象,它是一个对象模板,它定义了对象的结构,包括对象的属性名和属性值的类型。
let obj: { name: string, age: number } = {
name: 'Jack',
age: 18
};
var sites = {
site1: "Runoob",
site2: "Google",
sayHello: function () { } // 类型模板
};
sites.sayHello = function () {
console.log("hello " + sites.site1);
};
sites.sayHello();
此外对象也可以作为一个参数传递给函数,如下实例:
function disp(obj: { name: string, age: number }) {
console.log("name: " + obj.name);
console.log("age: " + obj.age);
}
disp({ name: 'Jack', age: 18 });
鸭子类型
鸭子类型 是动态类型的一种风格,是多态(polymorphism)的一种形式。
在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。
可以这样表述:
"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。"
在鸭子类型中,关注点在于对象的行为能做什么,而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,
他接受一个类型为“鸭子”的对象,并调用它的“走”和“叫”的方法。在使用鸭子类型的语言中,这样的一个函数可以接收一个任意类型的对象,
并调用他的“走”和“叫”的方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的“走”和“叫”方法的
对象都可以被函数接受的这种行为引入了以上表述,这种决定类型的方式因此得名。
interface IPoint {
x:number
y:number
}
function addPoints(p1:IPoint,p2:IPoint):IPoint {
var x = p1.x + p2.x
var y = p1.y + p2.y
return {x:x,y:y}
}
// 正确
var newPoint = addPoints({x:3,y:4},{x:5,y:1})
// 错误
var newPoint2 = addPoints({x:1},{x:4,y:3})
TypeScript命名空间
TypeScript中的命名空间使用 namespace 关键字来定义,语法格式如下:
namespace SomeNameSpaceName {
export interface ISomeInterfaceName { }
export class SomeClassName { }
}
以上定义了一个命名空间 SomeNameSpaceName 如果我们需要在SomeNameSpaceName命名空间外部访问 SomeClassName 类,
需要在类和接口添加 export 关键字。
要在另一个命名空间的调用语法格式为:
SomeNameSpaceName.SomeClassName;
如果一个命名空间在一个单独的TypeScript文件中则应使用 /// 引用它,
语法格式如下:
/// <reference path = "SomeFileName.ts" />
IShape.ts 文件代码:
namespace Drawing {
export interface IShape {
draw();
}
}
Circle.ts 文件代码:
/// <reference path = "IShape.ts" />
namespace Drawing {
export class Circle implements IShape {
public draw() {
console.log("Circle is drawn");
}
}
}
Triangle.ts 文件代码:
/// <reference path = "IShape.ts" />
namespace Drawing {
export class Triangle implements IShape {
public draw() {
console.log("Triangle is drawn");
}
}
}
TestShape.ts 文件代码:
/// <reference path = "IShape.ts" />
/// <reference path = "Circle.ts" />
/// <reference path = "Triangle.ts" />
function drawAllShapes(shape:Drawing.IShape) {
shape.draw();
}
drawAllShapes(new Drawing.Circle());
drawAllShapes(new Drawing.Triangle());
嵌套命名空间
命名空间支持嵌套,我们可以在一个命名空间里面嵌套命名空间,如下实例:
namespace SomeNameSpaceName {
export namespace Shapes {
export class Triangle {
// ...
}
export class Square {
// ...
}
}
}
成员的访问使用点号(.)来实现,如下实例:
SomeNameSpaceName.Shapes.Triangle
TypeScript模块
TypeScript模块的设计理念是可以更换的组织代码。
模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里的变量、函数和类等在模块外部是不可见的,除非明确的使用export到处他们。类似地,我们必须通过
import导入其他模块导出的变量、函数、类等。
两个模块之间的关系是通过在文件级别上使用import和export建立的。
模块使用模块加载器去导入其他的模块。在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。
大家最熟知的JavaScript模块加载器是服务于 Node.js 的 CommonJS 和服务于 Web 应用的 Require.js。
此外还有有 SystemJs 和 Webpack。
模块导出使用关键字export,语法格式如下
// 文件名称: SomeInterface.ts
export interface SomeInterface {
// ...
}
需要在另一个文件使用SomeInterface时,需要使用import关键字导入
import someInterfaceRef = require('./SomeInterface');
导出
任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出。
// 导出变量
export var someVar = 123;
// 导出函数
export function someFunc() {}
// 导出类
export class SomeClass {}
// 导出接口
export interface SomeInterface {}
// 导出类型别名
export type SomeType = string | number;
TypeScript声明文件
TypeScript作为JavaScript的超集,在开发过程中不可避免的要引用其他第三方的JavaScript的库。虽然直接应用可以调用库的类的方法,
但是却无法使用TypeScript注入类型检查等特性功能。为了解决这个问题,需要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生
了一个描述JavaScript库和模块信息的声明文件。通过饮用这个声明文件,就可以借用TypeScript的各种特性来使用库文件了。
声明文件
声明文件是以.d.ts
为后缀的文件,用来帮助开发者在TypeScript中使用已有的JavaScript的工具包。声明文件可以让TypeScript
编译器识别新的语法,这些新的语法是JavaScript的语法扩展,比如:类型检查、ES6语法支持等。声明文件本身不会被编译成JavaScript,
所以不会影响到真正的运行时代码。
声明文件的书写方式
declare var jQuery: (selector: string) => any;
jQuery('#foo');
声明文件的书写方式有以下几种:
- 全局声明:全局声明用于描述全局变量、函数、类等。例如:
// 在全局范围内声明一个变量
declare var myGlobalVar: number;
// 在全局范围内声明一个函数
declare function myGlobalFunc(param: string): number;
- 模块声明:模块声明用于描述模块的类型信息。例如:
// 声明一个模块
declare module 'myModule' {
export function myFunc(param: string): number;
}
- 命名空间声明: 命名空间声明用于描述命名空间的类型信息。例如:
// 声明一个命名空间
declare namespace MyNamespace {
function myFunc(param: string): number;
}
- 类和接口声明: 类和接口声明用于描述类和接口的类型信息。例如:
// 声明一个类
declare class MyClass {
myMethod(param: string): number;
}
// 声明一个接口
declare interface MyInterface {
myProperty: number;
myMethod(param: string): number;
}
- 枚举类型声明:枚举类型声明用于描述枚举的类型信息。例如:
// 声明一个枚举类型
declare enum MyEnum {
FirstValue,
SecondValue,
}