布尔值 boolean
let isDone: boolean = false;
注意,使用构造函数 Boolean 创造的对象不是布尔值
let newBool: boolean = new Boolean(true);
// 编译报错: 不能将类型“Boolean”分配给类型“boolean”。“boolean”是基元,但“Boolean”是包装器对象。如可能首选使用“boolean”。ts(2322)
数字 number
let number: number = 6;
let notANumber: number = NaN;
字符串 string
let string: string = 'Tom';
let sentence: string = `my name is ${aString}`;
空值 void
void 类型的变量只能赋值为 undefined 和 null
let unusable: void = undefined;
可以用 void 表示没有任何返回值的函数
function alertName(): void {
alert('My name is Tom');
}
null 和 undefined
undefined 类型的变量只能被赋值为 undefined,null 类型的变量只能被赋值为 null
let u: undefined = undefined;
let n: null = null;
与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:
let u: undefined;
let num: number = u;
let num2:number = undefined;
// 编译合法 undefined是number的子类型
let unm2: void;
let num3: number = unm2;
// => 不合法 (void不是number的子类型)
任意值 any
any 用来表示允许赋值为任意类型
let anyType:any = 'seven';
anyType = 7;
在任意值上访问任何属性和方法都是允许的,即不做类型检查
let anyType:any = 'seven';
console.log(anyType.name().age)
// => 允许编译,但是js执行会报错
变量如果在声明的时候,未指定其类型, 也没有赋值, 那么它会被推断(类型推论)为任意值类型而完全不被类型检查
let something;
// 等价于 let something: any;
something = 'seven';
something = 7;
数组
可理解为相同类型的一组数据,数组类型有多种定义方式
1,类型 + 方括号( type [ ] )
这种方式定义的数组项中不允许出现其他的类型
let list: number[] = [1, 2, 3];
2,数组泛型 Array < type >
let list: Array<number> = [1, 2, 3];
枚举 enum
['enəm]
枚举类型用于取值被限定在一定范围内的场景,如一周只有7天,一年只有4季等。
枚举初始化
枚举初始化可以理解为给枚举成员赋值。每个枚举成员都需要带有一个值,在未赋值的情况下, 枚举成员会被赋值为从 0
开始, 步长为 1 递增的数字:
enum Weeks {Mon, Tue, Wed, Thu, Fri, Sat, Sun};
console.log(Weeks['Mon']); // => 0
console.log(Weeks[0]); // => 'Mon'
console.log(Weeks.Tue); // => 1
手动赋值时, 未赋值的枚举成员会接着上一个枚举项递增(初始化):
enum Weeks {
Mon, Tue, Wed, Thu = 2, Fri, Sat = -1.5, Sun
};
console.log(Weeks['Mon']); // => 0
console.log(Weeks.Wed); // => 2
console.log(Weeks.Thu); // => 2
console.log(Weeks.Fri); // => 3
console.log(Weeks.Sun); // => -0.5
上例中,未手动赋值的 Wed 和手动赋值的 Thu 取值重复了,但是 TypeScript 并不会报错,该种情况可能会引起取值错误,所以使用的时候最好避免出现取值重复的情况。
TypeScript 支持 数字 的和基于字符串的枚举。
数字枚举
enum Weeks {
Sun, Mon, Tue, Wed, Thu, Fri, Sat
};
字符串枚举
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
接口 Interfaces
接口(Interfaces)是一个很重要的概念,可以理解为一种规范或者约束,用来描述 对象(object) 的形状 或者对 类(class) 的行为 进行抽象。对类的行为抽象将在后面 类与接口 一章中介绍,下面主要介绍对对象的形状进行描述。
接口定义
使用 interface 定义接口, 接口名称一般首字母大写,定义接口的时候,只定义声明即可,不包含具体内容:
// 定义一个接口 Person
interface Person {
name: string;
age: number;
}
// 定义一个个变量,它的类型是 Person
let tom: Person = {
name: 'Tom',
age: 25
};
实现接口的时候,要实现里面的内容,定义的变量比接口少了或多了属性都是不允许的:
let tom: Person = {
name: 'tom'
}
// => 编译报错,少了age属性
可选属性
使用 ? 代表可选属性, 即该属性可以不存在, 但不允许添加未定义的属性
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'tom'
}
// age是可选属性
任意属性
定义了任意属性后可以添加未定义的属性,并可以指定属性值的类型
interface Person03 {
name: string;
age?: number;
[propName: string]: any;
}
let tom04: Person03 = {
name: 'Tom',
age: 25,
gender: 'male'
};
定义了任意属性,那么确定属性和可选属性都必须是它的子属性
interface Person {
name: string;
age?: number;
[propName: string]: string;
}
// 编译报错:Person定义了一个任意属性,其值为string类型。则Person的所有属性都必须为string类型,而age为number类型
只读属性 readonly
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
let person: Person = {
id: 100,
name: 'tom',
}
person05.id = 90;
// => 编译报错:id为只读, 不可修改
let person2: Person = {
name: 'welson',
age: 2
}
// => 编译报错:给对象 person2 赋值,未定义只读属性id
person2.id = 1;
// => 编译报错:id为只读, 不可修改
函数类型接口
// 只有参数列表和返回值类型的函数定义, 参数列表里的每个参数都需要名字和类型
interface SearchFunc {
(source: string, subString: string): boolean;
}
函数
函数声明
function sum(x: number, y: number): number {
return x + y;
}
输入多余的(或者少于要求的)参数,是不被允许的
sum(1, 2, 3);
// 编译报错:多了1个参数
匿名函数(函数表达式)
let mySum = function (x: number, y: number): number {
return x + y;
};
上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum
,是通过赋值操作进行类型推论而推断出来的。如果需要我们手动给 mySum
添加类型,则应该是这样:
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
// 注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>
用接口定义函数的形状
interface FuncAdd {
(value: number, increment: number): number
}
let add: FuncAdd;
add = function(value: number, increment: number): number {
return value + increment;
}
// 函数的参数名不需要与接口里定义的名字相匹配
let add2: FuncAdd;
add2 = function(a: number, b: number) {
return a + b;
}
可选参数
可选参数必须接在必需参数后面,换句话说,可选参数后面不允许再出现必须参数了
function addNum(a: number, b: number, c? :number): number {
if(c) {
return a + b + c;
} else {
return a + b;
}
}
console.log(add(1, 2));
默认参数
类比 ES6 中的默认值
function add(a: number = 1, b: number): number {
return a + b;
}
console.log(add(undefined, 1));
剩余参数
类比 Es6 中对象展开
interface AddFunc {
(num1: number, ...rest: number[]): number
}
let add: AddFunc;
add = function(a: number, ...rest: number[]): number {
let result = a;
rest.map(v => result += v);
return result;
}
console.log(add(1,2,3,4));
类 class
同ES6 的 class
相关概念
- 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
- 对象(Object):类的实例,通过
new
生成 - 面向对象(OOP)的三大特性:封装、继承、多态
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如
Cat
和Dog
都继承自Animal
,但是分别实现了自己的eat
方法。此时针对某一个实例,我们无需了解它是Cat
还是Dog
,就可以直接调用eat
方法,程序会自动判断出来应该如何执行eat
- 存取器(getter & setter):用以改变属性的读取和赋值行为
- 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如
public
表示公有属性或方法 - 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
类的定义
使用 class
定义类,使用 constructor
定义构造函数。
通过 new
生成新实例的时候,会自动调用构造函数。
class Animal {
name:string; // 定义属性
constructor(name) {
this.name = name; // 属性赋值
}
sayHi() {
return `我叫 ${this.name}`;
}
}
let cat = new Animal('Tom');
console.log(cat.sayHi()); // 我叫 Tom
类的继承
使用 extends
关键字实现继承,子类中使用 super
关键字来调用父类的构造函数和方法。
class Cat extends Animal {
color: string;
constructor(name, color) {
super(name); // 调用父类Animal的 constructor(name)
this.color = color
}
sayHi() {
// 调用父类的 sayHi();
return super.sayHi() + '我是一只'+ this.color + ' 色的猫,';
}
}
let c = new Cat('Tom', '橘黄'); // Tom
console.log(c.sayHi()); // 我叫 Tom,我是一只橘黄色的猫;
let cat2 = new Cat('Jerry');
cat2.color = '黑';
console.log(c.sayHi()); // 我叫 Jerry,我是一只黑色的猫;
存取器
使用 getter 和 setter 可以改变属性的赋值和读取行为:
class Animal {
name:string;
constructor(name) {
this.name = name;
}
get name() {
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack
实例属性和方法
js中的属性和方法:
// js中
function Person(name) {
this.name = name; // 实例属性
this.eat = function(){ console.log('eat') }; // 实例方法
}
Person.age = 19; // 静态属性
Person.sleep = function(){ console.log('sleep') }; // 静态方法
// 访问实例方法和属性:
var tom = new Person('tom');
console.log(tom.name) // tom
tom.eat();
tom.sleep() // error: tom.sleep is not a function
// 访问静态方法和属性:
console.log(Person.age); // 19
Person.sleep();
Person.eat(); // error: Person.eat is not a function
ES6 中实例的属性只能通过构造函数中的 this.xxx
来定义:
class Animal {
constructor(){
this.name = 'tom';
}
eat() {}
}
let a = new Animal();
console.log(a.name); // tom
ES7 提案中可以直接在类里面定义:
// ts
class Animal {
name = 'tom';
eat() {}
}
let a = new Animal();
console.log(a.name); // Jack
静态属性和方法
ES7 提案中,可以使用 static
定义一个静态属性或方法。静态方法不需要实例化,而是直接通过类来调用:
// ts
class Animal {
static num = 42;
static isAnimal(a) {
return a instanceof Animal;
}
}
console.log(Animal.num); // 42
let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function
访问修饰符
public
公有属性或方法,可以在任何地方被访问到,默认所有的属性和方法都是 public
的
private
私有属性或方法,不能在声明它的类的外部访问,也不可以在子类中访问
protected
受保护的属性或方法,它和 private
类似,区别是它可以在子类中访问
class Person {
public name:string;
private idCard:number;
protected phone:number;
constructor(name,idCard,phone) {
this.name = name;
this.idCard = idCard;
this.phone = phone;
}
}
let tom = new Person('tom',420000,13811110000);
console.log(tom.name) // tom
console.log(tom.idCard)
// error:Property 'idCard' is private and only accessible within class 'Person'.
console.log(tom.phone)
// error:Property 'phone' is protected and only accessible within class 'Person' and its subclasses
class Teacher extends Person {
constructor(name,idCard,phone) {
super(name,idCard,phone);
console.log(this.name)
console.log(this.phone)
console.log(this.idCard)
// error:Property 'idCard' is private and only accessible within class 'Person'.
}
}