typescript入门笔记自用2(类&函数)

75 阅读5分钟

简单的例子:

class Greeter {
   greeting: string;
   constructor(message: string) {
       this.greeting = message;
   }
   greet() {
       return "Hello, " + this.greeting;
   }
}

let greeter = new Greeter("world");

继承

类当然会有继承功能,使用关键字extends进行继承,下面是一个简单的例子

class Son extends Greeter {
    hello() :string {
        return "hello"
    }
}
let son = new Son('son')
console.log(son.hello());
console.log(son.greet());

上面的例子里,Son类不包含构造函数,但当子类(超类)有构造函数时,必须在构造函数内调用super,它会调用基类的构造函数。而且,在构造函数内访问this之前,必须调用super

class Animal {
    name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

class Horse extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 45) {
        console.log("Galloping...");
        super.move(distanceInMeters);
    }
}

let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");

sam.move();
tom.move(34);

public, private, protected

默认情况下为public
private的意思就是私有化,使得变量只能在当前这个类中访问。在子类中,虽然仍能super构造方法,但无论是在子类的其他方法里,还是直接访问,都访问不到

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
    log() {
        console.log(this.name); //name为私有属性,只能在"animal"类中访问
    }
}

protected protectedprivate类似,不同的是protected在派生类中仍可以访问,在实例中也是不可访问的

class Animal {
    protected name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
    log() {
        console.log(this.name); //success
    }
}

let sam = new Snake("Sammy the Python");
console.log(sam.name);// name是受保护的,只能在类"Animal"中及其子类访问

readonly 和 参数属性

参数属性就是方便直接进行初始化,感觉还是写出来比较好,不过要看得懂别人的用法,贴个官网例子

class Octopus {
    readonly name: string;
    readonly numberOfLegs: number = 8;
    constructor (theName: string) {
        this.name = theName;
    }
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

//参数属性
class Octopus {
    readonly numberOfLegs: number = 8;
    constructor(readonly name: string) {
    }
}

存取器

属性的getset方法,注意一点,当属性有get方法而没有set方法时自动转变为readonly,使用这个版本输出要调到ECMAScript5或更高

let passcode = "secret passcode";

class Employee {
    private _fullName: string;

    get fullName(): string {
        return this._fullName;
    }

    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            console.log("Error: Unauthorized update of employee!");
        }
    }
}

let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    alert(employee.fullName);
}

抽象类

抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。

abstract class Department {

    constructor(public name: string) {
    }

    printName(): void {
        console.log('Department name: ' + this.name);
    }

    abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Department {

    constructor() {
        super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
    }

    printMeeting(): void {
        console.log('The Accounting Department meets each Monday at 10am.');
    }

    generateReports(): void {
        console.log('Generating accounting reports...');
    }
}

let department: Department; // 允许创建一个对抽象类型的引用
department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
department.generateReports(); // 错误: 方法在声明的抽象类中不存在

把类当作接口使用

class Point {
    x: number = 1;
    y: number = 2;
}

interface pointPlus extends Point {
    z: string
}

函数

先来看一个完整函数类型的例子

let myFunc: (x: number, y: number) => number = 
    function(x: number, y: number) {
        return x + y
    }

类型推论

赋值语句一边指定了类型,另一边就会自动推断出类型

let myFunc: (x: number, y: number) => number = 
    function(x, y) {
        return x + y
    }

可选参数和默认参数

一个是加?,一个是指定默认值,两者的共同作用就是让参数可选,二者的类型推论相同。
使用默认参数时,要传递undefind作为参数,表示使用默认值。

function buildName(firstName = "Will", lastName: string) {
    return firstName + " " + lastName;
}

let result1 = buildName("Bob");                  // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");         // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams");     // okay and returns "Will Adams"

剩余参数

剩余参数可以有多个,也可以没有,我们创建一个数组存放,前面用...省略号,类似拓展运算符

function buildName(firstName: string, ...restOfName: string[]): string {
    return firstName + " " + restOfName.join(" ")
}

let res = buildName("chen", "zhi", "cong", "sb")
console.log(res);

this与箭头函数

我们知道,js在函数调用时才会指定this(非箭头函数),对于下面一个例子,this指向了window,所以会报错

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        return function() {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13}; //error
        }
    }
}
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
alert("card: " + pickedCard.card + " of " + pickedCard.suit);

解决这个问题,可以用箭头函数,它会保留上下文this

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

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

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

但是现在this.suits[pickedSuit]的类型仍然为any,也就是ts没有对它进行类型推论,解决这一问题我们需要显式指出this,在下面的例子中,this被显式指定为Deck接口类型的

interface Card {
    suit: string;
    card: number;
}
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};
        }
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert("card: " + pickedCard.card + " of " + pickedCard.suit);

函数重载

需求:根据传入参数的不同,返回不同类型的结果。在js中,我们无需做额外的操作,需要返什么返回什么即可,但在ts中,我们利用函数重载

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): any {
    // Check to see if we're working with an object/array
    // if so, they gave us the deck and we'll pick the card
    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);