typescript2(基础篇1)

JavaScript 的一个超集,主要提供了类型系统对 ES6 的支持

基础类型

布尔值:

1
2
3
// new Boolean()通过不了,其它基本类型也一样(除了null和undefined)
//
let isDone: boolean = false;

数字:

1
2
3
//可以用es6的进制表示数字,被编译为十进制
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;

字符串:

1
2
3
//支持模板字符串
let myName: string = 'Tom';
let sentence: string = `Hello, my name is ${myName}.

void

1
2
3
4
5
6
//和java一样,void一般用在函数上,表示没有返回值
//放在变量上没什么用,只能将它赋值为 undefined 和 null
function alertName(): void {
alert('My name is Tom');
}
let unusable: void = undefined || null;

undefined和null

1
2
3
//undefined 和 null 是所有类型的子类型
let u: undefined;
let num: number = u;

any

1
2
3
4
//可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
//和js没啥区别,对重构js有用。
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;

类型推论

1
2
3
4
5
6
7
8
9
10
//定义之后不赋值,推断为any
let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
//等价于
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;

联合类型

1
2
3
4
5
6
7
8
9
10
11
//ts在编译的时候不清楚联合类型的变量到底什么类型,只能访问它的所有类型里的共有方法和属性
//toString()是string和number的共有方法
function getString(something: string | number): string {
return something.toString();
}
//类型检查
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 编译时报错

类型断言

1
2
3
4
5
6
7
8
9
10
11
12
13
//将一个联合类型断言为更具体的类型,这样就不会报错了
//不要理解为类型转换,不能断言一个联合类型中不存在的类型
function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
// 使用类型断言对之进行一个强制的转换
// 一般多使用as jsx里面只能使用这种
let len1: number = (value1 as string).length;
let len2: number = (<string>value1).length;

数组:

1
2
3
4
5
6
7
//这里的数组里面数值必须和前面的一样
//支持泛型
let fibonacci: number[] = [1, 1, 2, 3, 5];
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
//定义时会约束数组一些方法的参数,如下会报参数错误
let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8');

元组:

1
2
3
4
5
6
7
8
9
//元组是解决数组只能合并相同类型的方案
//通过索引方式赋值可以只赋值其中某些项,但是当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项
let tom: [string, number] = ['Tom', 25];
let tom: [string, number];
tom[0] = 'Tom';
//越界按联合类型处理
let tom: [string, number];
tom = ['Tom', 25];
tom.push('male');

枚举:

1
2
3
4
5
6
7
8
9
10
11
//默认从0开始编号,可手动赋值。
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
//编译(可以正向找值,也可以反向找值)
Days[Days["Sun"] = 0] = "Sun";
let d: Days = Days.Sun; //0
let day: string = Days[0]; //Sun
//手动赋值的枚举项可以不是数字,此时需要使用类型断言来让 tsc 无视类型检查
enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"};
//上面都是常数项,还支持计算所得项
//紧接在计算所得项后面的项必须手动赋值
enum Color {Red, Green, Blue = "blue".length};

接口

行为抽象,需要由类去实现具体的行为,其实就是规范

1
2
3
4
5
6
// 这里对label属性的值做一个string的校验
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
let myObj = { size: 10, label: "Size 10 object" };
printLabel(myObj);

我们通过接口来对上面的代码进行一个重写:

1
2
3
4
5
6
7
8
9
interface LabelledValue{
label:string,
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
// 上面声明之后,那么myObj这里就必须要有这个属性
let myObj = { size: 10, label: "Size 10 object" };
printLabel(myObj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//赋值的时候,变量的形状必须和接口的形状保持一致,多一个,少一个都不行
interface Person {
name: string;
age: number;
}

let tom: Person = {
name: 'Tom',
age: 25
};
//支持可选属性,定义可选的属性可以不存在
interface Person {
name: string;
age?: number;
}

任意类型

1
2
3
4
5
6
//当我们需要一个任意类型时
interface Person {
name: string;
age?: number;
[propName: string]: any;
}

一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。A be assignable to B – A是B的子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface Square {
color: string;
area: number;
}
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
function createSquare(config: SquareConfig): Square {
let newSquare = { color: "white", area: 1000 };
// 这里判断一下可选属性
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
console.log(createSquare({}));
// 如果我们这里不小心把属性名称写错了,但是由于上面有额外的属性检测,这里就不会报错
console.log(createSquare({ colour: "blue", width: 100 }));
console.log(createSquare({ color: "yello" }));

只读属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
//此处第一次给对象赋值时,一定要给id赋值,否则报错
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
//赋值会报错
tom.id = 9527;

ts里面还有一种只读的数组泛型:

1
2
3
4
5
6
7
let a: number[] = [1,2,3,4]
let ro: ReadonlyArray<number> = a;
// 只读的数组泛型,下面这些操作都会犯罪
ro[0] = 1;
ro.push(5);
// 正确操作回去可以使用类型断言
a = ro as number[]

可索引类型的接口:

1
2
3
4
5
6
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
interface NotOkay {
[x: number]: Dog;
[x: string]: Animal;
//数字类型索引的索引值一定要是字符串索引类型索引值的子类型
//因为当用number做索引时,js会将其转化为string,然后再去做索引
// 这个地方按照下面写会报错,因为数字签名(Animal)并不是字符串签名(Dog)的子类型
// [x: string]: Dog;
// [x: number]: Animal;
}

设置一个只读类型的索引签名:

1
2
3
4
5
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!

class类型

规则和java一样

这里要注意一下:es6中实例属性的新写法 – 实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 构造器接口
interface ClockInterface {
// 这两个类型是实例类型
currentTime: Date;
setTime(d: Date);
}

// 实例接口,用构造器签名(signatrue)定义的一个接口
interface ClockContructor {
new (hour: number, minute: number);
}

// 如果这个地方去继承ClockContructor的话会报error
// 当类实现一个接口时,只对实例部分进行类型检查,而constructor存在于静态部分。
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}

可是如果我们想去使用一个实例类型的话,代码是可以这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 此处的tick()不用进行类型检查,在类中为静态方法(class.prototype)
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}

let digital = createClock(DigitalClock, 10, 15);
let analog = createClock(AnalogClock, 5, 38);

接口继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
// 先这样继承下来,后续实例对多个接口变量进行访问
let square = {} as Square;
square.color = "blue";
square.sideLength = 12;
square.penWidth = 15;

混合类型(接口既可以当成对象使用,也可以当函数用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Counter {
// 函数类型,有个start参数,返回一个string类型
(start: number): string;
// 一些额外的属性
interval: number;
reset():void;
}
// 定义一个函数,返回一个Counter类型的接口
function getCounter():Counter{
let counter = (function(star:number){}) as Counter;
counter.interval = 123;
counter.reset = function(){
}
return counter;
}
let c = getCounter();
// 因为这就是个这样的函数
c(10);
// 它也可以当成对象使用
c.reset();
c.interval = 5.0;
-------------要说再见啦感谢大佬的光临~-------------