TypeScript入门学习-01篇

现在很多第三方库都改用ts来写了,如果遇到问题去看相关源码,看不懂人家那一堆都语法是啥意思,就尴尬了,所以还是先把TypeScript的语法过一遍,起码要能看懂人家的代码表达的一个大概意思。

基础类型

定义一个变量,都需要写成这种格式:let [变量: 类型] = 值;
和JS基本一样的是:

1
2
3
4
5
let isDone: boolean = false;
let decLiteral: number = 6;
let name: string = `bob`;
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];

和JS相比比较不一样的:

元组 Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

1
2
3
4
5
let x: [string, number]; 
x = ['hello', 10] // OK
x = [10, 'hello']; // Error, Initialize it incorrectly
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'

枚举 enum

默认从0开始编号,也可以手动的指定成员的数值,作用是可以由枚举的值得到它的名字

1
2
3
enum Color {Red = 1, Green = 4, Blue = 5}
let c: Color = Color.Green;
let colorName: string = Color[4]; // Green

Any

编程阶段还不清楚类型的变量指定一个类型

1
2
3
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

Void

void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void

1
2
3
function warnUser(): void {
console.log("This is my warning message");
}

类型断言

1
2
3
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length; // 方式1
let strLength: number = (someValue as string).length; // 方式2

对象的类型——接口

定义一个接口,然后用的时候和 [let 变量:类型 = 值] 是一个感觉;感觉接口是对非基础类型对一个称呼

1
2
3
4
5
6
7
8
9
10
11
interface Person {
name: string;
age: number;
}
//在typescript里,
// [变量: 类型]
| |
let tom: Person = {
name: 'Tom',
age: 25
};

可选属性

1
2
3
4
5
6
7
8
9
10
11
12
interface SquareConfig {
color?: string; // 可传可不传对属性 用 ?
width?: number;
}
函数定义 传参 : 函数返回值
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
...
return newSquare; // 这个行对应函数返回值类型;
}

let mySquare = createSquare({color: "black"});

只读属性

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

额外的属性检查

如果 SquareConfig带有上面定义的类型的color和width属性,并且还会带有任意数量的其它属性,那么我们可以这样定义它:

1
2
3
4
5
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}

函数类型

1
2
3
4
5
6
7
8
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}

可索引的类型

定义了StringArray接口,它具有索引签名。 这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值。TypeScript支持两种索引签名:字符串和数字;

1
2
3
4
5
6
7
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

索引签名设置为只读,这样就防止了给索引赋值:

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

类类型

implements关键字, 当一个类实现了一个接口时,只对其实例部分进行类型检查。因为createClock的第一个参数是ClockConstructor类型,在createClock(AnalogClock, 7, 32)里,会检查AnalogClock是否符合构造函数签名。

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
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}

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, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

继承接口

支持多接口继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Shape {
color: string;
}

interface PenStroke {
penWidth: number;
}

interface Square extends Shape, PenStroke {
sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

混合类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Counter {
(start: number): string; //函数传参
interval: number; // 属性
reset(): void; // 方法
}
// 函数名 : 函数返回值
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

泛型

这一块就是我想看人家都源码,看不懂不得已放弃研究的地方。
identity函数叫做泛型,因为它可以适用于多个类型。不同于使用Any,会丢失传入和返回的类型关联。
从代码中理解意思:

1
2
3
4
5
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString"); // 指定泛型T是string
let output = identity("myString"); // 编译器会根据传入的参数自动地帮助我们确定T的类型 这两种调用方式都OK

类型变量T会帮助我我们捕获传入的类型,例如,传入的是number还是string,之后返回T,这样传入的值和返回的值就是一样了。

1
2
3
4
5
#### 使用泛型变量
function loggingIdentity<T>(arg: Array<T>): Array<T> {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}

理解:泛型函数loggingIdentity,接收类型参数T和参数arg,它是个元素类型是T的数组,并返回元素类型是T的数组。 如果我们传入数字数组,将返回一个数字数组,因为此时 T的的类型为number。 这可以让我们把泛型变量T当做类型的一部分使用,而不是整个类型,增加了灵活性。

泛型类型

1
2
3
4
5
6
7
8
9
interface GenericIdentityFn {
<T>(arg: T): T;
}

function identity<T>(arg: T): T {
return arg;
}

let myIdentity: GenericIdentityFn = identity;

泛型类

泛型类使用( <>)括起泛型类型,跟在类名后面。
类的所有属性都在使用相同的类型,没有什么去限制它,如下只能使用number类:

1
2
3
4
5
6
7
8
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; };