前言
npm i -g typescript
$tsc -v
tsc test.ts // 编译
node test.js // 执行
const hello: string = 'Hello World!'
console.log(hello)
1 对象+基本类型
1.1 ts是一种面向对象的编程语言
-
面向对象有两个概念: 对象 + 类
-
类:一个模板,描述一类对象的行为和状态
-
对象: 类的一个实例
-
class Site {
name(): void {
console.log('yaoyao')
}
}
var obj = new Site();
obj.name();
1.2 ts的基础类型
ts的基础类型 | 描述 |
---|---|
any | 任意类型 |
number | 数字 |
string | 字符 |
boolean | 布尔 |
enum | 枚举 |
void | 用于标识方法返回值的类型,表示该方法没有返回值 |
null | 表示对象值缺失 |
undefined | 用于初始化变量为一个未定义的值 |
never | 其他类型的子类型,代表从不会出现的值 |
1.2.1 any
- any是ts针对编程时类型不明确的变量使用的一种数据类型
- 变量的值会动态改变时
- 任意值类型可以让这些变量跳过编译阶段的类型检查
let x: any = 1; // 数字类型
x = 'i am who i am'; // 字符串类型
x = false; // 布尔类型
let x: any = 4;
x.ifExists(); // 正确,这里不会检查ifExists方法是否存在
x.toFixed(); // 正确
let arrayList: any[] = [1, false, 'fine'];
1.2.2 空校验(strictNullChecks)特性
- 在ts中启用严格的空校验(strictNullChecks)特性,可以使得null undefined只能被赋值给void或本身对应的类型
let x: number;
x = 1; // 运行正确
x = undefined; // 运行错误
x = null; // 运行错误
let x: number | null | undefined;
x = 1; // 运行正确
x = undefined; // 运行正确
x = null; // 运行正确
1.2.3 never
- never 是其他类型的子类型,代表不会出现的值
- 声明为never类型的变量只能被never类型所赋值
- 在函数中通常表现为抛出异常或无法执行到终止点(无限循环)
let x: never;
let y: number;
x = 123; // 运行错误
x = (() => { throw new Error('exception') })(); // 运行正确
// 运行正确 never类型可以赋值给数字类型
y = (() => { throw new Error('exception') })();
function error(message: string): never {
throw new Error(message);
}
function loop(): never {
while(true) {}
}
2 ts函数
/**
* 函数定义 函数返回值
* 1 return_type是返回值的类型
* 2 返回值的类型需要与函数定义的返回类型(return_type)一致
* */
// function function_name() [:return_type] {
// // 执行代码
// [return value;]
// }
function greet(): string {
return 'hello world';
}
function caller() {
var msg = greet();
console.log(msg)
}
caller()
2.1 带参函数
/**
* 带参数函数
* param1、param2 为参数名
* datatype为参数类型
*/
// function func_name(param1[: datatype], param2[: datatype]) {
// }
function add(x: number, y: number): number {
return x + y;
}
console.log(add(1, 2));
2.2 可选参数
- 在ts函数里,如果我们定义了参数,则必须传入这些参数
- 除非将这些参数设置为可选,可选参数使用问号标识
?
- 可选参数必须跟在必需参数后面
function buildName(firstName: string, lastName: string) {
return firstName + ' ' + lastName;
}
let result1 = buildName('Bob'); // 错误
let result2 = buildName('Bob', 'Adams', 'sr.'); // 错误
let result3 = buildName('Bob', 'Adams'); // 正确
function buildName2(firstName: string, lastName?: string) {
return firstName + ' ' + lastName;
}
let result1 = buildName('Bob'); // 正确
let result2 = buildName('Bob', 'Adams', 'sr.'); // 错误
let result3 = buildName('Bob', 'Adams'); // 正确
2.3 默认参数
- 参数不能同时设置为可选和默认
// function function_name(param1[: type], param2[: type] = default_value) {
// }
function calculate_discount(price: number, rate: number = 0.50) {
var discount = price * rate;
console.log('计算结果:', discount);
}
calculate_discount(1000);
calculate_discount(1000, 0.30);
2.4 剩余参数
- 允许我们将一个不确定数量的参数作为一个数组传入
- 函数的最后一个命名参数restOfName以...为前缀,它将成为一个由剩余参数组成的数组,索引值从0到resOfName.length
function buildName(firstName: string, ...resOfName: string[]) {
return firstName + ' ' + resOfName.join('');
}
let employeeName = buildName('Joseph', 'Same', 'Lucas', 'Andy');
function addNumbers(...nums: number[]) {
var i;
var sum: number = 0;
for(i = 0; i < addNumbers.length; i++) {
sum = sum + nums[i];
}
console.log('和为:', sum)
}
addNumbers(1, 2, 3)
addNumbers(10, 10, 11, 111, 1111)
2.5 匿名函数
- 在程序运行时动态声明,除了没有函数名外,其他的与标准函数一样
// var res = function([arguments]) { ... }
var msg = function() {
return 'hello world';
}
console.log(msg())
var res = function(a: number, b: number) {
return a * b;
};
console.log(res(12, 2))
##### 匿名函数自调用
```ts
(function() {
var x = 'hello';
console.log(x)
})()
2.6 构造函数
/**
* 构造函数
* arg1,arg2,...argN: 参数列表
* functionBody: 一个含有包括函数定义的js语句的字符串
*/
// var res = new Function([arg1[, arg2[, ...argN]],] functionBody)
var myFunction = new Function('a', 'b', 'return a * b');
var x = myFunction(4, 3);
console.log(x);
2.7 递归函数
- 在函数内调用函数本身
function factorial(number) {
if(number <= 0) {
return 1;
} else {
return (number * factorial(number - 1))
}
}
console.log(factorial(6));
2.8 Lambda函数,即箭头函数
// ([param1, param2, ...param n]) => statement;
// ([param1, param2, ...param n]) => { // 代码块 }
var foo = (x: number) => 10 + x;
console.log(foo(100));
var foo2 = (x: number) => {
x = 10 + x;
console.log(x);
}
foo2(100);
var func = x => {
if(typeof x == 'number') {
console.log(x + '是一个数字');
} else if(typeof x == 'string') {
console.log(x + '是一个字符串');
}
}
func(12);
func('Tom');
var disp = () => {
console.log('Function invoked');
}
disp();
2.9 函数重载
- 重载是方法名相同,而参数不同,返回类型可以相同也可以不同
- 每个重载的方法都必须有一个独一无二的参数类型列表
- 参数类型不同,则参数类型应设置为any
- 参数数量不同,可以将不同的参数设置为可选
2.9.1 参数类型不同
function disp(string): void;
function disp(number): void;
2.9.2 参数数量不同
function disp(n1: number): void;
function disp(x: number, y: number): void;
2.9.3 参数类型顺序不同
function disp(n1: number, s1: string): void;
function disp(s: string, n: number): void;
function disp2(s1: string): void;
function disp2(n1: number,s1: string): void;
function disp2(x: any, y?: any): void {
console.log(x);
console.log(y);
}
disp2('abc');
disp2(1, 'xyz');
3 元组
-
数组中元素类型一般是相同的,如果存储的元素类型不同,则需要使用元组
-
元组中允许存储不同类型的元素,元组可以作为参数传递给函数
-
语法:var tuple_name = [value1, value2, value3, ...value n]
var mytuple = [10, 'yaoyao'];
var mytuple1 = [];
mytuple1[0] = 120;
mytuple1[1] = 220;
3.1 访问元组
var mytuple2 = [10, 'yaoyao'];
console.log(mytuple2[0]);
3.2 解构元组
var a = [10, 'yaoyao'];
var [b, c] = a;
console.log(b);
console.log(c);
4 联合类型
- 联合类型可通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值
注意:只能赋值指定类型,否则报错
- 语法: Type1|Type2|Type3
var val: string | number;
val = 12;
console.log('数字为:' + val);
val = 'yaoyao';
console.log('字符串为:' + val);
// var val:string|number;
// val = true; // 报错
4.1 将联合类型作为函数参数使用
function disp(name: string | string[]) {
if(typeof name == 'string') {
console.log(name);
} else {
var i;
for(i = 0; i < name.length; i++) {
console.log(name[i]);
}
}
}
disp('yaoyao');
console.log('输出数组...');
disp(['hello', 'yaoyao', 'are', 'you', 'ok'])
4.2 联合类型数组
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 = ['yaoyao', 'hello', 'world'];
console.log('**字符串数组**');
for(i = 0; i<arr.length; i++) {
console.log(arr[i]);
}
5 接口interface
- 接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要具体类去实现,三方可通过这组抽象方法调用,让具体的类执行具体的方法
接口不能转换为js,它只是ts的一部分
- 定义:interface interface_name {}
interface IPerson {
firstName: string,
lastName: string,
sayHi: ()=> string
}
var customer: IPerson = {
firstName: 'Tom',
lastName: 'Hanks',
sayHi: (): string => 'Hi there'
}
console.log('Customer 对象 ');
console.log(customer.firstName);
console.log(customer.lastName);
console.log(customer.sayHi());
var employee: IPerson = {
firstName: 'Zhou',
lastName: 'yaoyao',
sayHi: (): string => 'Hello!!'
}
console.log('Employee 对象 ');
console.log(employee.firstName);
console.log(employee.lastName);
console.log(employee.sayHi());
6 对象
- 对象是包含一组键值对的实例。值可以是标量、函数、数组、对象等
var obj_name = {
key1: 'value1', // 标量
key2: 'value',
key3: function() {},
key4: ['content1','content2']
}
var sites1 = {
site1: 'zhou',
site2: 'yaoyao'
}
console.log(sites1.site1);
console.log(sites1.site2);
6.1 类型模板
var sites2 = {
site1: 'zhou',
site2: 'yaoyao',
sayHello: function() {} // 类型模板
}
sites2.sayHello = function() {
console.log('hello' + sites2.site2);
}
sites2.sayHello()
6.2 对象也可以作为一个参数传递给函数
var sites3 = {
site1: 'zhou',
site2: 'yaoyao'
}
var invokesites = function(obj: {site1: string, site2: string}) {
console.log('site1:'+obj.site1);
console.log('site2:'+obj.site2);
}
invokesites(sites3)
6.3 鸭子类型
-
关注点在于对象的行为,能做什么;而不是关注对象所属的类型
-
鸭子类型是动态类型的一种风格,是多态的一种形式
-
在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由“当前方法和属性的集合”决定
-
多态就是允许方法重名 参数或返回值可以是父类型传入或返回。
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, y}
}
var newPoint = addPoints({x:3, y:4},{x:5, y:1})
console.log('newPoint', newPoint);
// var newPoint2 = addPoints({x:3},{x:5, y:1}) // 错误
7 类
-
ts是面向对象的js
-
类描述了所创建的对象共同的属性和方法
-
ts支持面向对象的所有特性
-
定义: calss class_name { // 类作用域 }
-
类可以包含以下几个模块(类的数据成员):
-
001 字段--字段是类里面声明的变量。字段表示对象的有关数据
-
002 构造函数--类实例化时调用,可以为类的对象分配内存
-
003 方法--方法为对象要执行的操作
7.1 创建类的数据
- this关键字表示当前类实例化的对象
- 构造函数的参数名与字段名相同
class Car {
// 字段
engine: string;
// 构造函数
constructor(engine: string) {
this.engine = engine
}
// 方法
disp(): void {
console.log('发动机为:' + this.engine);
}
}
7.2 创建实例化对象
- var object_name = new class_name([arguments])
- 类中的字段属性和方法可以使用.来访问
- obj.field_name
- obj.function_name()
var obj = new Car('Engine 1')
class Car2 {
engine: string;
constructor(engine: string) {
this.engine = engine
}
disp(): void {
console.log('函数中显示发动机型号:' + this.engine);
}
}
var obj = new Car('xxxsy1');
console.log('读取发动机型号:' + obj.engine);
obj.disp()
7.3 类的继承extends
- 子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他都可以继承
- ts一次只能继承一个类,不支持继承多个类;但ts支持多重继承(A继承B,B继承C)
- 语法: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 obj11 = new Circle(233);
obj11.disp();
class Root {
str: string;
}
class Child extends Root {}
class Leaf extends Child {}
var obj22 = new Leaf()
obj22.str = 'Hello'
console.log(obj22.str);
7.4 继承类的方法重写
- supper关键字是对父类的直接引用,该关键字可以引用父类的属性和方法
class PrinterClass {
doPrint(): void {
console.log('父类的doPrint()方法');
}
}
class StringPrinter extends PrinterClass {
doPrint(): void {
super.doPrint()
console.log('子类的dePrint()方法');
}
}
#### 7.5 static关键字
* 用于定义类的数据成员(属性和方法)为静态,静态成员可以直接通过类名调用
```ts
class StaticMen {
static num: number;
static disp(): void {
console.log('num值为:' + StaticMen.num);
}
}
StaticMen.num = 12
StaticMen.disp() // num值为12
7.6 instanceof运算符
- 用于判断对象是否是指定的类型,如果是返回true,否则返回false
class PersonI {}
var objI = new PersonI()
var isPersonI = objI instanceof PersonI;
console.log('objI对象是PersonI类实例化来的吗?' + isPersonI);
7.7 访问控制修饰符
-
ts中可使用访问控制符来保护类、对象、方法和构造方法的访问
-
ts支持3种不同的访问权限:
-
001 public(默认) 公有,可以在任何地方被访问
-
002 protected 受保护,可以被其自身以及其子类和父类访问
-
003 private 私有,只能被其定义所在的类访问
class Encapsulate {
str1: string = 'Hello';
private str2: string = 'world';
}
var objII = new Encapsulate()
console.log(objII.str1);
// console.log(objII.str2);
7.8 类和接口
- 类可以实现接口,使用关键字implements
interface ILoan {
interest: number
}
class AgriLoan implements ILoan {
interest: number
rebate: number
constructor(interest: number, rebate: number) {
this.interest = interest
this.rebate = rebate
}
}
var objIl = new AgriLoan(10, 1)
console.log('利润为:' + objIl.interest + ',抽成为:' + objIl.rebate);
8 命名空间
- 关键字 namespace
- 目的:解决重名问题
- 命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,且在不同名字空间中的含义是互不相干的
- 在外部调用命名空间中的类和接口,需要在类和接口添加extends关键字
- 如果一个命名空间在一个单独的ts文件中,则应使用三斜杠///应用它
/// <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())
// tsc --out nameSpaceTest.js TestShape.ts
// node nameSpaceTest.js
8.1 嵌套命名空间
namespace namespace_name1 {
export namespace namespace_name2 {
export class class_name {}
}
}
- 成员的访问使用点好
.
来实现
// Invoice.ts文件
namespace Runoob {
export namespace invoiceApp {
export class Invoice {
public calculateDiscount(price: number) {
return price * 4
}
}
}
}
// IvoiceTest.ts文件
/// <reference path = 'Invoice.ts'/>
var invoice = new Runoob.invoiceApp.Invoice()
console.log(invoice.calculateDiscount(500));
9 声明文件
9.1 栗子
- js中 使用jQuery
- $('#foo);
- jQuery('#foo')
- ts中
- jQuery('#foo') // index.ts(1,1): err: Cannot find name 'jQuery'
- 使用declare关键字来定义它的类型,帮助ts判断我们传入的参数类型
declare var jQuery: (selector: string) => any;
jQuery('#foo')
declare定义的类型只会用于编译时的检查,编译结果会被删除
9.2 声明文件
- 声明文件以.d.ts为后缀
- 声明文件不包含实现,只是类型声明
- 声明文件或模板的语法格式: declare module Module_Name {}
- ts引入声明文件语法格式: ///
// CalcThirdPartyJsLib.js文件
var Yaoyao;
(function(Yaoyao) {
var Calc = (function() {
function Calc() {}
})
Calc.prototype.doSum = function(limit) {
var sum = 0;
for(var i=0; i<=limit; i++) {
sum = sum + i
}
return sum;
}
Yaoyao.Calc = Calc;
return Calc;
})(Yaoyao || (Yaoyao = {}))
var test = new Yaoyao.Calc()
//Calc.d.ts文件
declare module Yaoyao {
export class Calc {
doSum(limit: number): number
}
}
// 把声明文件加入到ts中
// CalcTest.ts文件
/// <reference path = 'Calc.d.ts'/>
var obj = new Yaoyao.Calc()
console.log(obj.doSum(10));