typescript2(基础篇2)

类的一些基本示例:

1
2
3
4
5
6
7
8
9
10
11
class Greteer{
greeting: string
constructor(message:string){
this.greeting = message;
}
greet(){
return 'Hello,' + this.greeting
}
}
let greeter = new Greteer('world');
greeter.greet();

继承

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
28
29
30
31
32
class Animals {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance:number){
console.log(`${this.name} moved ${distance}m`);
}
}
class Snake extends Animals{
constructor(name:string){
super(name);
}
move(distance:number = 5){
console.log('Slitering ....');
super.move(distance);
}
}
class Hourse extends Animals{
constructor(name:string){
super(name);
}
move(distance:number=45){
console.log('Galloping...');
// 去给父类里面传递参数
super.move(distance);
}
}
let sam = new Snake('Sammy');
let tom:Animals = new Hourse('Tommy');
sam.move();
tom.move(12);

publicprivateprotected

1
2
3
4
5
6
7
8
9
10
//默认为public
class Animal0{
public name:string
public constructor(name:string) {
this.name = name
}
public move(distance:number = 0){
console.log(`${this.name} moved ${distance} m`);
}
}
1
2
3
4
5
6
7
8
9
//private
class Animal1{
private name: string
constructor(name:string){
this.name = name;
}
}
// 这个地方就会报错
new Animal1('ni').name;

理解private

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// private属性只能在类Animal中使用。
// ts中如果有private和protected成员,一个类型的private和protected成员,另一个类型也必须有,并且源自同一个定义,这两个类型才是兼容的。
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
constructor() { super("Rhino"); }
}

class Employee {
private name: string;
constructor(theName: string) { this.name = theName; }
}
//这里其实有个类型推论。
let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");

animal = rhino;
animal = employee; // Error: 'Animal' and 'Employee' are not compatible

理解protected

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 和private基本差不多,只不过私有成员不能在子类中访问,但是protected成员可以在子类中访问。
// 看一个栗子
class Person {
protected name: string;
protected constructor(theName: string) { this.name = theName; }
}

class Employee extends Person {
private depart: string;

constructor(name: string, depart: string) {
super(name);
this.depart = depart;
}
getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.depart}`;
}
}

let zsy = new Employee("zsy", "R & d");
let xiaolaji = new Person("xiaolaji", "R & d");// err. 类"Person"是受保护的。

readonly用来设置一些只读属性

1
2
3
4
5
6
7
8
9
class Person0{
readonly name:string
constructor(name:string) {
this.name = name;
}
}
let john = new Person0('john');
// 只读的类就不能修改了,下面就会报error了
john.name = 'xxx';

存取器

setget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 
class Animal0 {
private _name: string;
constructor(name: string) {
this._name = name;
}
public move(distance: number = 0) {
console.log(`${this._name} moved ${distance} m`);
}
get name() {
return this._name;
}
set name(newName: string) {
this._name = newName;
}
}
class Hippo extends Animal0 {
constructor() {
super("Hippo");
}
}

let hh = new Hippo();
console.log(hh.name);

我们编译的时候将目标设置为ES5,采用下面这个命令:

1
2
tsc index.ts --target es5
node index.js

类的静态成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 其实就是编译后把static成员直接赋值给了Grid。
class Grid {
static origin = { x: 0, y: 0 };
scale: number;
constructor(scale: number) {
this.scale = scale;
}

claculateDistanceFromOrigin(point: { x: number, y: number }) {
let xDist = point.x - Grid.origin.x;
let yDist = point.y - Grid.origin.y;
return Math.sqrt(xDist * xDist + yDist * yDist) * this.scale;
}
}

let grid1 = new Grid(1.0);
let grid2 = new Grid(5.0);
console.log(Grid.origin);// { x: 0, y: 0 }
console.log(grid1.claculateDistanceFromOrigin({ x: 3, y: 4 }));
console.log(grid2.claculateDistanceFromOrigin({ x: 3, y: 4 }));

抽象类

抽象类作为其他派生类的基类使用,他们是不能被实例化的。

1
2
3
4
5
6
abstract class Animal {
abstract makeSound(): void
move(): void {
console.log('raoming the earth...');
}
}

类的高级技巧

函数

基本示例

demo

1
2
3
4
5
6
7
// 
function add(x, y) {
return x + y;
}
let myAdd = function(x, y) {
return x + y;
};

添加参数类型

1
2
3
4
5
6
7
unction add (x:number,y:number):number{
return x + y;
}
// 也可以变量类型,其实不设的话调用的时候也可以推断出来的
let myAdd:(baseValue:number,increValue:number) => number = function(x:number,y:number):number{
return x + y;
}

this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
// return function() {
// 这个地方改为箭头函数,因为他是函数创建的时候的this值
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickSuit = Math.floor(pickedCard / 13);
return {
// 这里this在ts里面会被推断为any,即suit: any
suits: this.suits[pickSuit],
card: pickedCard % 13
};
};
}
};
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log(`card: ${pickedCard.card} of ${pickedCard.suits}`);

解决上面的suitsany的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Deck {
suits: string[];
cards: number[];
createCardPicker(this: Deck): () => Card;
}

let deck: Deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
// NOTE: The function now explicitly specifies that its callee must be of type Deck
createCardPicker: function(this: Deck) {
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);

return {suit: this.suits[pickedSuit], card: pickedCard % 13};
}
}
}

this在回调函数里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ts里面的this参数作为"伪"参数在函数参数列表的第一项
interface UIElement {
// this: void 表示onclick希望是个不使用this的函数。
addClickListener(onclick: (this: void, e: Event) => void): void;
}

class Handler {

info: string;
onClickBad(this: Handler, e: Event) {
this.info = e.type;
}
}

let h = new Handler();
let uiElement: UIElement = {
addClickListener() {}
}
uiElement.addClickListener(h.onClickBad); // error

arrow函数在ts中依然可以解决this的坑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Handler {
info: string;
onClickGood(this: void, e: Event) {
// can't use `this` here because it's of type void!
console.log('clicked!');
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickGood);

// 如果要在函数里使用this,就用箭头函数,因为箭头函数的this其实是外部的this
class Handler {
info: string;
onClickGood = (e: Event) => { this.info = e.message }
}

重载

ts中的方法重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
if (typeof x == "object") {
let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {
let pickedSuit = Math.floor(x / 13);
return { suit: suits[pickedSuit], card: x % 13 };
}
}

let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }];
let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

泛型

基本示例

1
2
3
4
5
// 返回任何传入的值,T同时用来捕获用户的传入类型
function identity<T>(arg:T):T{
return arg;
}
// 这里适用于多个类型,不会像any一样丢失类型
1
2
3
4
5
6
7
8
// 返回任何传入它的值,T同来捕获用户的传入类型
function identity<T>(arg:T):T{
return arg;
}
// 编译器识别不了的话则可以使用这种形式
let output0 = identity<string>('mystring')
// 这里编译器会自动帮我们推断出传入的值的类型
let output = identity('Mystring')

推荐的第二种写法

1
2
3
4
5
function loginingIndetity<T>(arg:T[]):T[]{
// 如果arg:T这里就不会有length属性,我们将其修改为T[]
console.log(arg.length);
return arg;
}

声明泛型变量的两种方式

1
2
3
// 这表示myIdentity和myIdentity2都是一个接受类型为T的arg参数,并且返回值类型为T的函数
let myIdentity: <T>(arg: T) => T = identity;
let myIdentity2: { <T>(arg: T): T } = identity;

泛型和接口

1
2
3
4
5
interface G<T> {
(arg: T): T;
}

let myIdentity3: G<number> = identity;

这样的好处在于我们不用在接口里面去描述一个泛型函数了。

泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class GenricNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
// 会自动推导出add函数的参数和返回制类型
let myGenericNumber = new GenricNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
return x + y;
};
let stringNumberic = new GenricNumber<string>();
stringNumberic.zeroValue = "";
stringNumberic.add = function(x, y) {
return x + y;
};
console.log(stringNumberic.add(stringNumberic.zeroValue, "test"));

泛型约束

1
2
3
4
5
6
7
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}

也可以用泛型约束泛型

1
2
3
4
5
6
7
8
9
// 
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}

let x = {a: 1, b: 2, c: 3, d: 4};

getProperty(x, "a");
getProperty(x, "z");// error
-------------要说再见啦感谢大佬的光临~-------------