TypeScript 是由微软开发的一款开源的编程语言,TypeScript 是 Javascript 的超集,遵循最新的 ES6、ES5 规范,TypeScript 扩展了 JavaScript 的语法。TypeScript 更像后端 Java、C#这样的面向对象语言,可以让 JavaScript 开发大型企业项目。谷歌也在大力支持 Typescript 的推广,谷歌的 angular2.x+ 就是基于 Typescript 语法,最新的 Vue 、React 也可以集成 TypeScript。Nodejs 框架中的 Nestjs、midway 中用的就是 TypeScript 语法。
在CMD中打开
npm install -g [email protected]
变量格式一:
let 变量名: 变量类型 = 初始化值;
变量格式二:
let 变量名: 变量类型 | undefined;
变量名 = 变量值;
let flag: boolean = true;
console.log(flag);
整数型:
let num: number = 123;
console.log(num);
浮点型:
let num: number = 3.1415926;
console.log(num);
let str: string = "Hello,TypeScript";
console.log(str);
第一种定义数组的方式:以数字类型数组为例
let arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
console.log(arr);
第二种定义数组的方式:以数字类型数组为例
let arr: Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
console.log(arr);
元组属于数组的一种,元组中的元素可以不必全部保持类型一致!
let user: [number, string];
let userId = 10086;
let userName = "Nick";
let randomBoolean = true;
user = [userId, userName]; // 正确
user = [userId, randomBoolean]; // 错误
随着计算机的不断普及,程序不仅只用于数值计算,还更广泛地用于处理非数值的数据。在其它程序设计语言中,一般用一个数值来代表某一状态,这种处理方法不直观,易读性差。如果能在程序中用自然语言中有相应含义的单词来代表某一状态,则程序就很容易阅读和理解。也就是说,事先考虑到某一变量可能取的值,尽量用自然语言中含义清楚的单词来表示它的每一个值,这种方法称为枚举方法,用这种方法定义的类型称枚举类型。
枚举类型的定义:
enum 枚举名 {
标识符[= 整型常数/字符串],
标识符[= 整型常数/字符串],
...
标识符[= 整型常数/字符串],
};
枚举类型的示例:
enum Flag {
success,
error,
overtime
};
let s: Flag = Flag.overtime;
console.log(s);//2
代码解读:如果标识符没有赋值,它的值就是下标。
enum Flag {
success = 200,
error = 404,
overtime = 500
};
let s: Flag = Flag.overtime;
console.log(s);//500
代码解读:如果标识符已经赋值,它的值就是被赋的值。
enum Flag {
success,
error = 100,
overtime
};
let s: Flag = Flag.overtime;
console.log(s);//101
代码解读:如果标识符没有赋值,它的值就是下标,如果从中间突然指定了一个值,那么它之后的值都会从当前值开始重新计算。
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
let d: Direction = Direction.Up;
console.log(d);//UP
let n: null = null;
let u: undefined = undefined;
TypeScript 中的 any 类型表示任意数据类型。
enum Flag {
success,
error,
overtime
};
let flag: any = true;//布尔型
let num: any = 123;//数字型
let str: any = 'Hello,TypeScript';//字符型
let arr: any = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];//数组型
let tuple: any = [10086, 'Nick'];//元组型
let e: any = Flag.success;//枚举型
let n: any = null;//null型
let u: any = undefined;//undefined型
TypeScript 中的 void 类型表示没有任何类型,一般用于定义方法的时候方法没有返回值。
function success(): void {
console.log('执行成功了,我不需要返回值');
}
TypeScript 中的 never 类型是任何类型的子类型,也可以赋值给任何类型,但是没有类型是 never 的子类型或可以赋值给 never 类型, 即使 any 类型也不可以赋值给never。这意味着声明 never 类型的变量只能被 never 类型所赋值。
function error(): never {
throw new Error('抛出错误了');
}
TypeScript 中支持一个变量可以赋予多种不同的变量类型,多个变量类型使用 |
分隔。
let num: number | null | undefined;
num = 3;
console.log(num);
num = null;
console.log(num);
num = undefined;
console.log(num);
3.1、函数定义
函数是由一连串的子程序(语句的集合)所组成的,可以被外部程序调用,向函数传递参数之后,函数可以返回一定的值。
通常情况下,TypeScript 代码是自上而下执行的,不过函数体内部的代码则不是这样。如果只是对函数进行了声明,其中的代码并不会执行,只有在调用函数时才会执行函数体内部的代码。
函数格式一:
function 函数名(参数列表): 返回值类型 {
函数体 ...
[return 返回值;]
}
必选参数:在调用函数的时候,必须要传入的参数,参数列表里边的参数默认就是必选参数,只要在声明的时候写了参数,在传递的时候,就必须传入参数,而且,实参与形参的数量与类型要一致。
function getInfo(name: string, age: number): string {
return `${name} --- ${age}`;
}
console.log(getInfo("张三", 28)); // 正确
console.log(getInfo("张三")); // 错误
console.log(getInfo(28)); // 错误
必选参数:为了解决在函数传参的时候,某些参数可以不用传递,我们就需要可选参数了。
function getInfo(name: string, age?: number): string {
return `${name} --- ${age}`;
}
console.log(getInfo("张三", 28)); // 正确
console.log(getInfo("张三")); // 正确
console.log(getInfo(28)); // 错误
注意:可选参数必须配置到参数的最后面。
默认参数:为了解决在函数传参的时候,某些参数可以不用传递,但是我们又需要该参数的值,这时候我们就需要给这个参数设定一个默认值也叫初始化值,就得用到默认参数了。
function getInfo(name: string, age: number = 20): string {
return `${name} --- ${age}`;
}
console.log(getInfo("张三", 28)); // 正确
console.log(getInfo("张三")); // 正确
console.log(getInfo(28)); // 错误
注意:可选参数不能够进行初始化值的设定。
箭头函数其实就是简化了函数当作参数传递时匿名函数的写法,具体可参考ES6新特性。
setTimeout(function () {
console.log("匿名函数执行了...");
}, 1000);
setTimeout(() => {
console.log("箭头函数执行了...");
}, 1000);
class Person {
name: string;//属性,前面省略了public关键词
constructor(n: string) {//构造函数,实例化类的时候触发的方法
this.name = n;//使用this关键字为当前类的name属性赋值
}
run(): void {//方法
console.log(this.name+ "在跑步");
}
}
var p = new Person("张三");
p.run();
类的继承:在 TypeScript 中要想实现继承使用 extends
关键字,只要一旦实现了继承关系,那么子类中便拥有了父类的属性和方法,而在执行方法过程中,首先从子类开始找,如果有,就使用,如果没有,就去父类中找。类的继承只能单向继承。
TypeScript 里面定义属性的时候给我们提供了 三种修饰符
public:公有类型,在当前类里面、子类、类外面都可以访问
protected:保护类型,在当前类里面、子类里面可以访问,在类外部没法访问
private:私有类型,在当前类里面可以访问,子类、类外部都没法访问
注意:如果属性不加修饰符,默认就是公有(public)。
静态属性:被静态修饰符修饰的属性就是静态属性,静态属性可以通过类名直接调用。
静态方法:被静态修饰符修饰的方法就是静态方法,静态方法可以通过类名直接调用,但是在静态方法内部,不能直接调用当前类的非静态属性、非静态方法。
TypeScript 中的抽象类:它是提供其他类继承的基类,不能直接被实例化。
用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类(也就是其子类)中实现,abstract抽象方法只能放在抽象类里面。
我们常常使用抽象类和抽象方法用来定义标准。
多态:父类定义一个方法不去实现,让继承它的子类去实现 ,每一个子类有不同的表现,多态属于继承。
在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。 typescrip中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。
接口的用途就是对行为和动作进行规范和约束,跟抽象类有点像,但是,接口中不能有方法体,只允许有方法定义。
//对传入对象的属性约束,以下这个是一个属性接口
interface FullName {
firstName: string;
secondName: string;
}
function printName(name: FullName) {
console.log(name.firstName + "--" + name.secondName);
}
//传入的参数必须包含firstName、secondName
var obj = {
age: 20,
firstName: '张',
secondName: '三'
};
printName(obj);//正确
// printName("1213");//错误
//加密的函数类型接口
interface encrypt {
(key: string, value: string): string;
}
var md5: encrypt = function (key: string, value: string): string {
//模拟操作
return key + "----" + value;
}
console.log(md5("name", "zhangsan"));
var sha1: encrypt = function (key: string, value: string): string {
//模拟操作
return key + "====" + value;
}
console.log(sha1("name", "lisi"));
可索引接口就是对数组、对象的约束,不常用。
//可索引接口,对数组的约束
interface UserArr {
[index: number]: string
}
var arr1: UserArr = ["aaa", "bbb"];
console.log(arr1[0]);
//可索引接口,对对象的约束
interface UserObj {
[index: string]: string
}
var arr2: UserObj = { name: '张三', age: '21' };
console.log(arr2);
类类型接口就是对类的约束,它和抽象类抽象有点相似。
interface Animal {
name: string;
eat(str: string): void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat() {
console.log(this.name + "吃大骨头");
}
}
var d = new Dog("小狼狗");
d.eat();
class Cat implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat(food: string) {
console.log(this.name + "吃" + food);
}
}
var c = new Cat("小花猫");
c.eat("大老鼠");
接口可以继承接口,接口之间和抽象类之间的继承都是单向单继承,但是实现接口的子类可以实现多个接口。
简单来说,对于类、抽象类、接口继承只能单继承,但接口却可以多实现。
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件。
通俗理解:泛型就是解决类、接口、方法的复用性、以及对不特定数据类型的支持。
泛型类可以支持不特定的数据类型,要求传入的参数和返回的参数必须一致,T表示泛型,具体什么类型是调用这个方法的时候决定的。
//泛型接口
interface ConfigFn {
(value: T): T;
}
function getData(value: T): T {
return value;
}
var myGetData: ConfigFn = getData;
console.log(myGetData('20'));
//定义操作数据库的泛型类
class MysqlDb{
add(info: T): boolean {
console.log(info);
return true;
}
}
//想给User表增加数据,定义一个User类和数据库进行映射
class User {
username: string | undefined;
pasword: string | undefined;
}
var user = new User();
user.username = "张三";
user.pasword = "123456";
var md1 = new MysqlDb();
md1.add(user);
//想给ArticleCate增加数据,定义一个ArticleCate类和数据库进行映射
class ArticleCate {
title: string | undefined;
desc: string | undefined;
status: number | undefined;
constructor(params: {
title: string | undefined,
desc: string | undefined,
status?: number | undefined
}) {
this.title = params.title;
this.desc = params.desc;
this.status = params.status;
}
}
var article = new ArticleCate({
title: "这是标题",
desc: "这是描述",
status: 1
});
var md2 = new MysqlDb();
md2.add(article);
对修饰器的实验支持功能在将来的版本中可能更改。
在 “tsconfig” 或 “jsconfig” 中设置 “experimentalDecorators” 选项以删除此警告。
“experimentalDecorators”: true // 启用对ES7装饰器的实验性支持。
装饰器是一种特殊类型的声明,它能够被附加到类、方法、属性或参数上,可以修改类的行为,通俗的讲装饰器就是一个方法,可以注入到类、方法、属性或参数上来扩展类、方法、属性或参数的功能。常见的装饰器有:类装饰器、方法装饰器、属性装饰器、参数装饰器。
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参),装饰器是过去几年中JS最大的成就之一,已是ES7的标准特性之一。
类装饰器:普通装饰器(无法传参)
function logClass(params: any) {
console.log(params);//params就是当前类
params.prototype.apiUrl = "我是动态扩展的属性";
params.prototype.run = function () {
console.log("我是动态扩展的方法");
}
}
@logClass
class HttpClient {
}
var http: any = new HttpClient();
console.log(http.apiUrl);
http.run();
类装饰器:装饰器工厂(可传参)
function logClass(params: string) {
return function (target: any) {
console.log(target);//target就是当前类
console.log(params);//params就是当前类传递进来的参数
target.prototype.apiUrl = params;
}
}
@logClass("http://www.baidu.com")
class HttpClient {
}
var http: any = new HttpClient();
console.log(http.apiUrl);
属性装饰器会被应用到属性描述上,可以用来监视、修改或者替换属性的值。
属性装饰器会在运行时传入下列2个参数:
//属性装饰器
function logProperty(params: any) {//params就是当前类传递进来的参数
return function (target: any, attr: any) {
console.log(target);
console.log(attr);
target[attr] = params;
}
}
class HttpClient {
@logProperty("http://www.baidu.com")
public url: any | undefined;
getData() {
console.log(this.url);
}
}
var http = new HttpClient();
http.getData();
方法装饰器会被应用到方法描述上,可以用来监视、修改或者替换方法定义。
方法装饰器会在运行时传入下列3个参数:
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据 ,传入下列3个参数:
装饰器执行顺序:属性 > 方法 > 方法参数 > 类
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
防止命名冲突
代码复用
高维护性
CommonJS => NodeJS、Browserify
AMD => requireJS
CMD => seaJS
模块功能主要由两个命令构成:export 和 import。
export 命令用于规定模块的对外接口
import 命令用于输入其它模块提供的功能