TypeScript 是 JavaScript 的一个超集,但是 TypeScript 与 JavaScript 之间又有什么样的区别呢?
TypeScript
TypeScript 是 Microsoft 开发和维护的一种面向对象的编程语言。它是 JavaScript 的超集,包含了 JavaScript 的所有元素,可以载入 JavaScript 代码运行,并扩展了 JavaScript 的语法。
TypeScript 具有以下特点:
- TypeScript 是 Microsoft 推出的开源语言,使用 Apache 授权协议
- TypeScript 增加了静态类型、类、模块、接口和类型注解
- TypeScript 可用于开发大型的应用
- TypeScript 易学易于理解
JavaScript 和 TypeScript 的主要差异
-
TypeScript 可以使用 JavaScript 中的所有代码和编码概念,TypeScript 是为了使 JavaScript 的开发变得更加容易而创建的。例如,TypeScript 使用类型和接口等概念来描述正在使用的数据,这使开发人员能够快速检测错误并调试应用程序
-
TypeScript 从核心语言方面和类概念的模塑方面对 JavaScript 对象模型进行扩展。
-
JavaScript 代码可以在无需任何修改的情况下与 TypeScript 一同工作,同时可以使用编译器将 TypeScript 代码转换为 JavaScript。
-
TypeScript 通过类型注解提供编译时的静态类型检查。
-
TypeScript 中的数据要求带有明确的类型,JavaScript不要求。
-
TypeScript 为函数提供了缺省参数值。
-
TypeScript 引入了 JavaScript 中没有的“类”概念。
-
TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
TypeScript 的优势
下面列举 TypeScript 相比于 JavaScript 的显著优势:
-
静态输入
静态类型化是一种功能,可以在开发人员编写脚本时检测错误。查找并修复错误是当今开发团队的迫切需求。有了这项功能,就会允许开发人员编写更健壮的代码并对其进行维护,以便使得代码质量更好、更清晰。 -
大型的开发项目
有时为了改进开发项目,需要对代码库进行小的增量更改。这些小小的变化可能会产生严重的、意想不到的后果,因此有必要撤销这些变化。使用TypeScript工具来进行重构更变的容易、快捷。 -
更好的协作
当发开大型项目时,会有许多开发人员,此时乱码和错误的机也会增加。类型安全是一种在编码期间检测错误的功能,而不是在编译项目时检测错误。这为开发团队创建了一个更高效的编码和调试过程。 -
更强的生产力
干净的 ECMAScript 6 代码,自动完成和动态输入等因素有助于提高开发人员的工作效率。这些功能也有助于编译器创建优化的代码。
ts程序组成部分:
1.模块
2.函数
3.变量
4.语句和表达式
5.注释
注意
- ts会忽略程序中出现的空格,制表符和换行符
- ts区分大小写
- 每行指令都是一段语句
- 同一行写多条语句一定要用分号,否则会报错
ts注释
单行注释://
多行注释:/**/
面向对象是一种对现实世界理解和抽象的方法
ts是一种面向对象的编程语言
面向对象主要有两个概念:
1.对象:对象是类的一个实例,有状态和行为
2.类:类是一个模板。它描述一类对象的行为和状态
方法:方法是类的操作的实现步骤
JavaScript的数据类型:
- boolean
- null
- undefined
- number
- string
- symbol
- object
ts的数据类型:
8. 任意类型 any
声明为any
的变量可以赋予任意类型的值
9. 数值类型 number let n : number = 1
10. 字符串类型 string let s : string ='你好'
11. 布尔类型 boolean let b : boolean = true
12. 数组类型 在元素后加[ ] let arr : number[ ] = [1,2]
或者使用数组泛型 let arr : Array<number> =[1,2]
-
原组 元组类型是用来表示已知元素数量和类型的数组,各
元素的类型不必相同,对应位置的类型需要相同let x : [string,number];
x=[‘你好’,12] -
枚举 enum 枚举类型用于定义数值集合
enum Color { red, green, blue};
let c : Color = Color.blue;
console.log(c) //打印2
- void void 用于标识方法返回值的类型,表示该方
法没有返回值
function hello( ) : void {
alert('void');
}
- null
null
表示对象值缺失 - undefined
undefined
用于初始化变量为一个未定义的值 - never
never
是其他类型子类型,表示从不会出现的值
any类型
是ts针对编程时类型不明确的变量使用的一种数据类型
常用于3 种情况
18. 变量的值会动态改变时,比如来自用户的输入,任意值类型可以让这些变量跳过编译阶段的类型检查
-
改写现有代码时,任意值允许在编译时可选择地包含或移除类型检查
-
定义储存任意类型数据的数组时
let arr : any[ ] = [1,'e',true]
null
在js中表示什么都没有,表示一个空对象的引用,typeof null 返回Object
undefined
在js中表示一个没有设置值的变量
null和undefined是其他类型的子类型(除了never)
never
其他类型的子类型,表示从来不会出现的值
表示声明为never类型的变量只能被never类型所赋值
在函数中它通常表现为抛出异常或无法执行到终止点
let x : never;
x =( () => {throw new Error('出错了')})( )
返回值为never的函数可以是抛出异常的情况
function error( ) : never {
throw new Error('出错了');
}
返回值为never的函数可以是无法被执行到的终点
function loop( ) : never {
while(true){ }
}
ts声明变量
变量是一种使用方便的占位符,用于引用计算机内存地址
我们可以把变量看做存储数据的容器
ts声明变量规则:
- 变量名称可以包含数字和字母
- 除了下划线_和美元符号$外,不能包含其他特殊字符,包括空格
- 变量名不能以数字开头
声明变量类型和初始值 var 变量名 : 类型 = 初始值
如: var myname : string = 'shijie';
没有设置初始值,变量初始值为undefined
没有设置类型。变量可以为任意类型
变量名不要使用name
否则会与DOM中的全局window
对象下的name
属性出现重名
类型断言
类型断言可以用来手动指定一个值得类型,即允许变量从一种类型更改为另一种类型
格式: <类型>值 或 值 as 类型
var str = '1'
var str2:number = <number> <any> str //str、str2 是 string 类型
console.log(str2) //1
ts是怎么确定单个断言是否足够?
之所以类型断言不被称为类型转换,是因为转换通常意味着某种运行时的支持。但是类型断言纯粹是一个编译时的语法,它也是一种为编译器提供关于如何分析代码的方法。
类型推断
当类型没有给出时,ts编译器利用类型推断来推断类型
如果有没有声明的变量,那么它被视为默认的动态any
类型
变量作用域
变量作用域指定了变量定义的位置
程序中变量的可用性由变量作用域决定
ts的三种作用域:
- 全局作用域
- 类作用域: 这个变量也可以称为字段 类变量声明在一个类
里,但在类的方法外面。该变量可以通过类的对
象来访问。静态变量可以通过类名直接访问。 - 局部作用域: 局部变量,局部变量只能在声明他的一个代码
块中使用。
如类变量
class Numbers{
a = 1; //类变量
static b = 3 ;//静态变量
sayName( ) : void{
var c =3; //局部变量,只能在方法中访问
}
}
访问a
var numbers = new Numbers();
numbers.a //1
访问b
Numbers.b //2
ts运算符
运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算。
TypeScript 主要包含以下几种运算:
- 算术运算符
- 逻辑运算符 用于测量变量或值的逻辑
- 关系运算符 用于计算结果是否为
true
或false
- 按位运算符 程序设计中对位模式按位或二进制数的一元和
二元操作。 - 赋值运算符 用于给变量赋值
- 三元/条件运算符 三元运算有 3 个操作数,并且需要判
断布尔表达式的值。该运算符的主要是决
定哪个值应该赋值给变量。
Test ? expr1 : expr2
- 字符串运算符 连接运算符 (
+
)
+
运算符可以拼接两个字符串 - 类型运算符
typeof
运算符
typeof
是一元运算符,返回操作数的数据类型。
instanceof
instanceof
运算符用于判断对象是否为指定的
类型
ts条件语句
TypeScript 条件语句是通过一条或多条语句的执行结果(True 或 False)来决定执行的代码块。
ts循环
for ( init; condition; increment ){
statement(s);
}
下面是 for 循环的控制流程解析:
- init 会首先被执行,且只会执行一次。这一步允许您声明并初始化任何循环控制变量。您也可以不在这里写任何语句,只要有一个分号出现即可。
- 接下来,会判断 condition。如果为 true,则执行循环主体。如果为 false,则不执行循环主体,且控制流会跳转到紧接着 for 循环的下一条语句。
- 在执行完 for 循环主体后,控制流会跳回上面的 increment 语句。该语句允许您更新循环控制变量。该语句可以留空,只要在条件后有一个分号出现即可。
- 条件再次被判断。如果为 true,则执行循环,这个过程会不断重复(循环主体,然后增加步值,再然后重新判断条件)。在条件变为 false 时,for 循环终止。
此外,TypeScript 还支持for…of
、forEach
、every
和some
循环。
break
语句
break
语句有以下两种用法:
- 当 break 语句出现在一个循环内时,循环会立即终止,且程序流将继续执行紧接着循环的下一条语句。
- 它可用于终止 switch 语句中的一个 case。
如果您使用的是嵌套循环(即一个循环内嵌套另一个循环),break 语句会停止执行最内层的循环,然后开始执行该块之后的下一行代码。
continue
语句
continue
语句有点像 break
语句。但它不是强制终止,continue 会跳过当前循环中的代码,强迫开始下一次循环。
对于 for 循环,continue 语句执行后自增语句仍然会执行。对于 while 和 do…while 循环,continue 语句重新执行条件判断语句。
函数是一组一起执行一个任务的语句。
函数
定义函数返回值类型
function function_name():return_type {
// 语句
return value;
}
可选参数
在 TypeScript 函数里,如果我们定义了参数,则我们必须传入这些参数,除非将这些参数设置为可选,可选参数使用问号标识 ?
举个?:
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // 正确
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确
可选参数必须跟在必需参数后面。
如果都是可选参数放哪都没关系
默认参数
我们也可以设置参数的默认值,这样在调用函数的时候,如果不传入该参数的值,则使用默认
一个参数不能同时设置为可选和默认。
举个?:
function calculate_discount(price:number,rate:number = 0.50) {
var discount = price * rate;
console.log("计算结果: ",discount);
}
剩余参数
有一种情况,我们不知道要向函数传入多少个参数,这时候我们就可以使用剩余参数来定义。
剩余参数语法允许我们将一个不确定数量的参数作为一个数组传入。
匿名函数自调用
匿名函数自调用在函数后使用 () 即可
递归函数
递归函数即在函数内调用函数本身。
Lambda 函数
Lambda 函数也称之为箭头函数。
函数重载
重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
TypeScript 与 JavaScript 类似,支持 Number 对象。
Number 对象方法:
- toExponential()
把对象的值转换为指数计数法。 - toFixed()
把数字转换为字符串,并对小数点指定位数。 - toLocaleString()
把数字转换为字符串,使用本地数字格式顺序。 - toPrecision()
把数字格式化为指定的长度。 - toString()
把数字转换为字符串,使用指定的基数。数字的基数是 2 ~ 36 之间的整数。若省略该参数, 则使用基数 10。 - valueOf()
返回一个 Number 对象的原始数字值。
String 对象用于处理文本(字符串)。
String 方法:
- charAt()
返回在指定位置的字符。 - charCodeAt()
返回在指定的位置的字符的 Unicode 编码。 - concat()
连接两个或更多字符串,并返回新的字符串。 - indexOf()
回某个指定的字符串值在字符串中首次出现的位置。 - lastIndexOf()
从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。 - localeCompare()
用本地特定的顺序来比较两个字符串。 - match()
查找找到一个或多个正则表达式的匹配。 - replace()
替换与正则表达式匹配的子串 - search()
检索与正则表达式相匹配的值 - slice()
提取字符串的片断,并在新的字符串中返回被提取的部分。 - split()
把字符串分割为子字符串数组。 - substr()
从起始索引号提取字符串中指定数目的字符 - substring()
提取字符串中两个指定的索引号之间的字符。 - toLocaleLowerCase()
根据主机的语言环境把字符串转换为小写, - toLocaleUpperCase()
据主机的语言环境把字符串转换为大写, - toLowerCase()
把字符串转换为小写 - toUpperCase()
把字符串转换为大写。 - toString()
返回字符串 - valueOf()
返回指定字符串对象的原始值。
TypeScript 声明数组的语法格式如下所示:
var array_name[:datatype]; //声明
array_name = [val1,val2,valn..] //初始化
或者直接在声明时初始化:
var array_name[:data type] = [val1,val2…valn]
如果数组声明时未设置类型,则会被认为是 any 类型,在初始化时根据第一个元素的类型来推断数组的类型。
Array 对象
我们也可以使用 Array 对象创建数组。
Array 对象的构造函数接受以下两种值:
表示数组大小的数值。
初始化的数组列表,元素使用逗号分隔值。
多维数组
一个数组的元素可以是另外一个数组,这样就构成了多维数组
数组方法:
- concat()
连接两个或更多的数组,并返回结果 - every()
检测数值元素的每个元素是否都符合条件 - filter()
检测数值元素,并返回符合条件所有元素的数组。 - forEach()
数组每个元素都执行一次回调函数。 - indexOf()
搜索数组中的元素,并返回它所在的位置 - join()
把数组的所有元素放入一个字符串。返回字符串 - lastIndexOf()
返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。 - map()
通过指定函数处理数组的每个元素,并返回处理后的数组。 - pop()
删除数组的最后一个元素并返回删除的元素。 - push()
向数组的末尾添加一个或更多元素,并返回新的长度。 - reduce()
将数组元素计算为一个值(从左到右) - reduceRight()
将数组元素计算为一个值(从右到左) - reverse()
反转数组的元素顺序 - shift()
删除并返回数组的第一个元素。 - slice()
选取数组的的一部分,并返回一个新数组 - some()
检测数组元素中是否有元素符合指定条件。 - sort()
对数组的元素进行排序。 - splice()
从数组中添加或删除元素。 - toString()
把数组转换为字符串,并返回结果。 - unshift()
向数组的开头添加一个或更多元素
ts元组
元组中允许存储不同类型的元素,元组可以作为参数传递给函数。
创建元组的语法格式如下:
var tuple_name = [value1,value2,value3,…value n]
元组运算
我们可以使用以下两个函数向元组添加新元素或者删除元素:
push()
向元组添加元素,添加在最后面。
pop()
从元组中移除元素(最后一个),并返回移除的元素。
TypeScript 联合类型
联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。
注意:只能赋值指定的类型,如果赋值其它类型就会报错。
Type1|Type2|Type3
声明一个联合类型:
var val:string|number
val = 12
console.log("数字为 "+ val)
val = "Runoob"
console.log("字符串为 " + val)
如果赋值其它类型就会报错:
var val:string|number
val = true
TypeScript 接口
接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
TypeScript 接口定义如下:
interface interface_name {
}
以下实例中,我们定义了一个接口 IPerson,接着定义了一个变量 customer,它的类型是 IPerson。
// customer 实现了接口 IPerson 的属性和方法。
TypeScript
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)
接口继承
接口继承就是说接口可以通过其他接口来扩展自己。
Typescript 允许接口继承多个接口。
继承使用关键字 extends
。
TypeScript 类
TypeScript 是面向对象的 JavaScript。
类描述了所创建的对象共同的属性和方法。
TypeScript 支持面向对象的所有特性,比如 类、接口等
this 关键字表示当前类实例化的对象。
继承类的方法重写
继承使用关键字 extends。
单接口继承语法格式:
Child_interface_name extends super_interface_name
多接口继承语法格式:
Child_interface_name extends super_interface1_name, super_interface2_name,…,super_interfaceN_name
类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。
其中 super
关键字是对父类的直接引用,该关键字可以引用父类的属性和方法。
类和接口
类可以实现接口,使用关键字implements
,
并将 interest 字段作为类的属性使用。
以下实例AgriLoan 类实现了 ILoan 接口:
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 )
泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
一、泛型的定义与使用
1.使用泛型变量
//泛型变量的使用
function identity<T>(arg:T):T{
console.log(typeof arg);
return arg;
}
let output1=identity<string>('myString');
let output2=identity('myString');
let output3:number=identity<number>(100);
let output4:number=identity(200);
//使用集合的泛型
function loggingIdentity<T>(arg:Array<T>):Array<T>{
console.log(arg.length);
return arg;
}
loggingIdentity([1,2,3]);
2.定义泛型函数
//泛型函数
function identity<T>(arg:T):T{
return arg;
}
let myIdentity:{<T>(arg:T):T}=identity;
3.定义泛型接口
//泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
4.定义泛型类
//泛型类
class GenericNumber<T>{
zeroValue:T;
add:(x:T,y:T)=>T;
}
let myGenericNumber=new GenericNumber<number>();
myGenericNumber.zeroValue=0;
myGenericNumber.add=function(x,y){return x+y;};
console.info(myGenericNumber.add(2,5));
let stringNumberic=new GenericNumber<string>();
stringNumberic.zeroValue='abc';
stringNumberic.add=function(x,y){return `${x}--${y}`};
console.info(stringNumberic.add('张三丰','王小明'));
二、泛型约束
1.使用 extends指定泛型类型的继承关系
//泛型约束 extends执行类型的继承关系
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity({ length: 2, value: 3 });
2.指定泛型类型为类类型的约束
//在泛型中使用类类型约束
function create<T>(c: { new (): T; }): T {
return new c();
}
一个更高级的例子,使用原型属性推断并约束构造函数与类实例的关系。
//泛型使用实例
class BeeKeeper {
hasMask: boolean;
}
class ZooKeeper {
nametag: string;
}
class Animal {
numLegs: number;
}
class Bee extends Animal {
keeper: BeeKeeper;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask; // typechecks!
访问控制修饰符
TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 3 种不同的访问权限。
- 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 是私有的
TypeScript 一次只能继承一个类,不支持继承多个类,但 TypeScript 支持多重继承(A 继承 B,B 继承 C)。
Typescript 中的对象必须是特定类型的实例。
TypeScript 命名空间
命名空间一个最明确的目的就是解决重名问题。
命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,它在不同名字空间中的含义是互不相干的。这样,在一个新的名字空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他名字空间中。
TypeScript 中命名空间使用 namespace
来定义,语法格式如下:
namespace SomeNameSpaceName {
export interface ISomeInterfaceName { } // export interface 只是对一个东西的声明(不能具体的操作)
export class SomeClassName { }
}
以上定义了一个命名空间 SomeNameSpaceName,如果我们需要在外部可以调用 SomeNameSpaceName
中的类类和接口,则需要在类和接口添加export
关键字。
如果一个命名空间在一个单独的 TypeScript 文件中,则应使用三斜杠 ///
引用它,语法格式如下:
/// <reference path = "SomeFileName.ts" />
嵌套命名空间
命名空间支持嵌套,即你可以将命名空间定义在另外一个命名空间里头。
成员的访问使用点号 . 来实现,如下实例:
Invoice.ts 文件代码:
namespace Runoob {
export namespace invoiceApp {
export class Invoice {
public calculateDiscount(price: number) {
return price * .40;
}
}
}
}
InvoiceTest.ts 文件代码:
/// <reference path = "Invoice.ts" />
var invoice = new Runoob.invoiceApp.Invoice();
tsconfig.json文件配置说明
{
"compilerOptions": { //生成相关说明,TypeScript编译器如何编译.ts文件。
"module": "commonjs", //指定模块代码生成:“none”、“commonjs”、“amd”、“system”、“umd”、“es2015”或“esnext”。
"target": "es6", //指定ECMAScript目标版本。许可值为“es3”、“es5”、“es6”、“es2015”、“es2016”、“es2017”、“es2018”或“esnext”。
"lib": ["es6"], //指定要包含在编译中的库文件。需要TypeScript 2.0或更高版本。
"sourceMap": true, //是否生成相应的“地图”的文件。
"rootDir": "src", //指定输入文件的根目录。使用——outDir控制输出目录结构。
"outDir": "out" //将输出结构重定向到目录。
}
}
命令:
npm install -g typescript //全局安装typescript
tsc //编译(一次性编译)
tsc -watch //编译并监视(每次保存待编译文件都会自动更新编译文件)
typescript使用其他javascript库
在.d.ts文件中定义它的声明
增加一个外部模块声明,保证TypeScript不报错。
外部模块使用declare来声明,比如我们为url模块编写一个外部声明node.d.ts:
declare module "url";
注意,此时我们没有定义导出类型,其类型为any。我们也可以为其定义类型:
declare module "url" {
export interface Url {
protocol?: string;
hostname?: string;
pathname?: string;
}
export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}
外部模块的使用跟一般模块类似,我们现在开头加一行声明,它会定义文件的预处理顺序。
/// <reference path="node.d.ts"/>
后面正常使用即可:
import * as URL from "url";
let myUrl = URL.parse("http://www.typescriptlang.org");
module关键词作用
- 一个模块若干个类
module egret {
export class XXX {
}
export class AAA{
}
}
在一个module下的不同类之间的相互调用不需要加模块名。
- 区分同类名
一个模块中有若干类,假如我写了两个类都叫 A 。那怎么区分呢,那么就使用这个module关键词将这两个类定义在不同模块(module)就行了
在使用module时定义一个类需要在前面加上export关键词。
没用使用export关键词,这样外界就无法访问这个类。