前端工作室专注前端开发
 博客首页

TypeScript中文网读书笔记

2021-03-16 08:00:00
JS基础与深入
44
文章图片

TypeScript中文网读书笔记,验证和实践相关知识要点。

TypeScript中文网读书笔记

第一章 五分钟上手TS

1.1 安装和使用

npm 安装命令

npm install -g typescript

安装后构建第一个ts文件,创建test.ts代码如下

function sayHi(person) {
  return "Hi, " + person;
}
let user "lee";
document.body.innerHTML = sayHi(user);

运行如下命令

tsc test.ts

可以生成一个test.js文件 加入test.html

<!DOCTYPE html>
<html>
    <head><title>TypeScript Test</title></head>
    <body>
        <script src="test.js"></script>
    </body>
</html>

打开该html试试 正常输出'Hi, lee'字样 修改test.ts添加类型注解

function sayHi(person: string) {
  return "Hi, " + person;
}
let user = 1;
document.body.innerHTML = sayHi(user);

执行命令

tsc test.ts

存在报错信息

Argument of type 'number' is not assignable to parameter of type 'string'.

但是js可以无报错正常运行输出"Hi, 1",仅仅是提醒可能无法正常运行而已,还是会生成js文件。 尝试使用接口,改写ts文件

interface Person {
  firstName: string,
  lastName: string
}
function sayHi(person: Person) {
  return "Hi, " + person.firstName + person.lastName;
}
let user = {firstName :'Lee',lastName: 'zw'};
document.body.innerHTML = sayHi(user);

同样运行命令后刷新html页面,页面输出"Hi, Leezw" 使用类改写这个例子

class Student {
  fullName: string;
  constructor(public firstName, public lastName) {
    this.fullName = firstName + lastName;
  }
}
interface Person {
  firstName: string,
  lastName: string
}
function sayHi(person: Person{
  return "Hi, " + person.firstName + person.lastName;
}
let user = new Student('Lee','zw');
console.log(user)  // 输出 Student {firstName: "Lee", lastName: "zw", fullName: "Leezw"}
document.body.innerHTML = sayHi(user);

页面输出 Hi, Leezw

第二章 基础类型

可以使用线上编译网站:https://www.w3cschool.cn/tryrun/runcode?lang=typescript测试

2.1 布尔值

let isDone : boolean = false;

2.2 数字

let x: number = 6;
let y: number = 0b1010; // 二进制

2.3 字符串

let name: string = "lee"

2.4 数组

第一种方式

let list: number[] = [1,2,3]
let arr: string[] = ['1']

第二种方式

let list: array<number> = [1,2,3]

2.5 元组tuple

let x: [string,number] ;
x = ['lee',1] // 正确
x = [1,1]  .// 报错 Type '[number, number]' is not assignable to type '[string, number]'.Type 'number' is not assignable to type 'string'.

报错指出元素位置

let x: [string,number] ;
x = ['lee',1]
console.log(x[0].substr(1));
console.log(x[1].substr(1)); // Property 'substr' does not exist on type 'number'.

访问越界元素时,会使用联合类型代替

x[3] = 'world' // 可以赋(string|umber)类型的值
x[4] = true // 报错 Type 'true' is not assignable to type 'string | number'.

2.6 枚举

enum Color {Red = 1, Green = 2, Blue = 4} // 不自定义时从0开始,每个都可以自定义
console.log(Color[2]); // Green
console.log(Color.Red); // 1

2.7 Any

不指定类型使用Any

let x: any;
x= 1;
x= 'lee';
x= true;

数组使用any类型

let list: any[] = [1,true,'lee'];

2.8 void

没有返回值时使用void

function add(a,b) : void{
    console.log(a + b)
}

或者设置值为null和undefined

let x: void = null;
x= undefined;

2.9 null和undefined

let u: undefined = undefined;
let n: null = null;

2.10 never

nener类型表示的是永不存在的值的类型。never是任何类型的子类型,也可以赋值给任何类型。没有类型是never的子类型或可以赋值给never类型(除了never本身之外),即使是any类型。

// 存在无法达到终点的函数
// 抛出错误的函数
function error():never{
    throw new Error('错误')
}
// 调用抛出错误或者说无法到达终点的函数
function fail(){
    return error()
}
// 死循环
function infiniteLoop():never{
    while(true){}
}

2.11 Objcet

object表示非原始类型,即除number、string、boolean、symbol、null和undefined之外的类型。

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

2.12 类型断言

形式一:

let someValue: any = "this is a string"; 
let strLength: number = (<string>someValue).length;

形式二:as语法(jsx只用as)

let someValue: any = "this is a string"; 
let strLength: number = (someValue as string).length;

第三章 变量声明

3.1 变量声明

let 和 const是js新的变量声明方式,const可以阻止变量的再次赋值,但是其属性可以赋值。

3.2 var声明

var a = 1;

访问函数中的变量,即闭包

function f() {
    var a = 10;
    return function g() {
        return a++; // a变量仍然可以被修改和访问
    }
}
var g = f();
console.log(g()) // 10
console.log(g()) // 11

3.2.1 var声明作用域怪异

var 声明有些奇怪的作用域

function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
        var x = 10;
    }

    return x;
}

console.log(f(true));  //  '10'
console.log(f(false)); // 'undefined'

因为相当于

function f(shouldInitialize: boolean) {
    var x
    if (shouldInitialize) {
        x = 10;
    }

    return x;
}

console.log(f(true));  // '10'
console.log(f(false)); // 'undefined'

存在变量提升的问题,同时再if等语句中无法形成外界无法访问的块作用域。

3.2.2 var 声明重复声明没有报错

var a = 1;
var a = 2;
console.log(a) // 2

再例如双层循环,应该输出100个数组结果只输出10个数字且没有报错信息

for(var i = 1; i< 10; i++){
    for(var i = 1; i< 10; i++){
        console.log(i)
    }
}
// 输出 1 2 3 4 5 6 7 8 9
console.log(i) // 11(循环外部可以访问)

3.2.3 var 声明捕获变量怪异

for(var i = 0;i< 10;i++){
    setTimeout(function(){console.log(i)},0)
} // 10 ... 10

等到setTimeout执行的时候,i已经变为10;

for(let i = 0;i< 10;i++){
    setTimeout(function(){console.log(i)},0)
} // 0 1 3 4 5 6 7 8 9

使用let可以解决

3.3 let 声明

let a = 1;

3.3.1 块级作用域

当用let 声明一个变量,它使用的是词法作用域或块作用域。在for循环、if花括号等外面无法访问得到。

if(true){
   let a = 1;
   console.log(a) // 1
}
console.log(a) // 报错 Cannot find name 'a'.
for(let i = 0;i< 10;i++){
    setTimeout(function(){console.log(i)},0)
} // 0 1 3 4 5 6 7 8 9
console.log(i) // Cannot find name 'i'.

catch语句中也一样

try{
    throw '错误'
}catch(e){
    console.log(e) // 错误
}
console.log(e) // Cannot find name 'e'.

3.3.2 暂时性死区

a++;
let a = 1;  
// Block-scoped variable 'a' used before its declaration.

没有变量提升

console.log(a) // Block-scoped variable 'a' used before its declaration.
let a = 1;

3.3.3 重复声明报错

let a = 0;
let a = 1; //报错 Cannot redeclare block-scoped variable 'a'.

函数参数重复使用

function f(x){
    let x = 1;
}
f() 
// 报错 Duplicate identifier 'x'.

在不同的块作用域中可以声明

for(let i = 1; i< 3; i++){
    for(let i = 1; i< 3; i++){
        console.log(i)
    }
} // 1 2 1 2(但是无法访问上一层的i了)
console.log(i) // 报错 Cannot find name 'i'.(for循环外部无法访问i)

3.3.4 块级作用域变量的获取

每次进入一个作用域时,它创建了一个变量的 环境。 就算作用域内代码已经执行完毕,这个环境与其捕获的变量依然存在。

for(let i = 0;i< 10;i++){
    setTimeout(function(){console.log(i)},0)
} // 0 1 3 4 5 6 7 8 9

3.4 const 声明

const a = 1

拥有与let同样的作用域规则,但是不能对它们重新赋值。

const a = 1;
a = 2;  // 报错 Cannot assign to 'a' because it is a constant or a read-only property.

可以修改const声明的对象,但是无法为该对象添加属性

const obj = {a: 0};
obj.a = 1; // 没问题
obj.b = 2; // 报错 Property 'b' does not exist on type '{ a: number; }'.

3.5 数组解构

解构数组

let arr = [1,2]
let [first,second] = arr;
console.log(first,second) // 1 2

交换变量值

let first = 1,second = 2;
[first,second] = [second,first]
console.log(first,second) // 2 1

作用于函数参数,并指定类型

function f([first,second]: [number,number]){
    console.log(first)
    console.log(second)
}
f([1,2])  // 1 2
f(['1','lee']) // 报错 Argument of type '[string, string]' is not assignable to parameter of type '[number, number]'.
  Type 'string' is not assignable to type 'number'.

数组里面使用...语法

let [first,...rest] = [1,2,3,4];
console.log(first,rest) // 1 [ 2, 3, 4 ]

当然也可以只解构其中一个,例如只解构第二个

let [,second] = [1,2,3,4];
console.log(second) // 2

3.6 对象解构

let obj = {
    a: 1,
    b: 2,
}
let {a,b} = obj;
console.log(a,b) // 1 2

对象解构时使用...语法

let obj ={
    a: 1,
    b: 2,
    c: 3
}
let {a,...rest} = obj;
console.log(a,rest) // 1 { b: 2, c: 3 }

3.6.1 属性重命名

let {a: a1} = {a: 1};
console.log(a1) // 1

如果需要指定类型

let {a: a1,b: b1}:{a: number,b:number} = {a: 1,b:2};
console.log(a1,b1) // 1 2

3.6.2 默认值

使用缺省值

let {a,b = 100}:{a: number,b?:number} = {a: 1}; // 类型为undefined或者number
console.log(a,b) // 1 100

3.7 展开

let arr1 = [1,2,3]
let arr2 = [...arr1,4,5];
console.log(arr2) // [ 1, 2, 3, 4, 5 ]

属于浅拷贝,即只拷贝第一层,第二层依然靠引用

let arr1:any[] = [{a:1},2,3]
let arr2 = [...arr1];
arr1[0].a = 0;
console.log(arr2) // [ { a: 0 }, 2, 3 ](第二层改变了)

展开对象

let obj1 = {
    a: 1,
}
let obj2 = {
    ...obj1,
    a: 0,
    b:2
}
console.log(obj2) // { a: 0, b: 2 }(obj2里的属性会覆盖obj1同样的属性)

调换位置

let obj1 = {
    a: 1,
}
let obj2 = {
    a: 0,
    b:2,
    ...obj1,
}
console.log(obj2) // { a: 1, b: 2 }(obj1里的属性会覆盖obj2同样的属性)

展开时仅包含其自身的可枚举属性,例如采用class新建对象时展开会丢失其方法

let obj1 = {
    a: 1,
    f(){return 1}
}
let obj2 = {
    a: 0,
    b:2,
    ...obj1,
}
console.log(Object.getOwnPropertyDescriptor(obj1,'f')) // {...} (证明是在自身且可枚举)
console.log(obj2.f()) // 1(方法未丢失)

采用class新建对象

class C {
  p = 12;
  m() {
      return 1
  }
}
let c = new C();
console.log(Object.getOwnPropertyDescriptor(c,'m')) // undefined
console.log(Object.getOwnPropertyDescriptor(c.__proto__,'m')) // { ...,,enumerable: true}(可枚举但不在自身上在原型上)
console.log(Object.getOwnPropertyDescriptor(C.prototype,'m')) // 同上
let clone = { ...c };
clone.p; // ok
clone.m(); // error!

第四章 接口

TS中接口的作用是为这些类型命名和为你的代码和第三方代码定义契约。

4.1 简单示例

interface person{
    name: string,
    age: number
}
function f(obj: person){
    console.log(obj.name,obj.age)
}
f({name:'lee',age: 18}) // lee 18

4.2 可选属性

interface person{
    name: string,
    age?: number
}
function f(obj: person){
    console.log(obj.name,obj.age)
}
f({name:'lee',age: 18}) // ee 18

加个?

4.3 只读属性

interface person{
    readonly name: string,
    readonly age?: number
}
function f(obj: person){
    console.log(obj) //  { name: 'lee' }
    obj.name = 'lzw' // 报错 Cannot assign to 'name' because it is a constant or a read-only property.
}
f({name:'lee'}) 

使用readonly后不可以修改属性 确保数组建立后不被修改ReadonlyArray

let list: ReadonlyArray<number> = [1,2,3];
console.log(list) // [ 1, 2, 3 ]
list[0]= 0; // 报错 Index signature in type 'ReadonlyArray' only permits reading.

包括修改某一项,push方法,length属性,甚至赋值都不可以。

4.4 额外的属性检查

interface person{
    name: string,
    age?: number
}
function f(obj: person){
    console.log(obj) //  { name: 'lee', job: 'worker' }
}
f({name:'lee',job: 'worker'})  // 报错 Argument of type '{ name: string; job: string; }' is not assignable to parameter of type'person'.Object literal may only specify known properties, and 'job' does not exist in type 'person'.

额外属性导致报错,想要绕过这个报错,最简便的方法是使用类型断言。

interface person{
    name: string;
    age?: number
}
function f(obj: person){
    console.log(obj) //  { name: 'lee', job: 'worker' }
}
f({name:'lee',job: 'worker'} as person)  // 不报错了

最佳方式是增加一个字符串索引签名

interface person{
    name: string,
    age?: number,
    [propName: string]: any
}
function f(obj: person){
    console.log(obj) //  { name: 'lee' }
}
f({name:'lee',job: 'worker'})  // 不报错了

最后一个方法,赋值给一个变量

interface person{
    name: string,
    age?: number,
}
function f(obj: person){
    console.log(obj) //  { name: 'lee' }
}
let obj2 = {name:'lee',job: 'worker'}
f(obj2)  // 使用变量逃避检查

4.5 函数类型

使用接口表示函数类型,需要给接口定义一个调用签名。

interface IncludeFunc{
    (str: string, word: string): boolean;
}
let myFunc: IncludeFunc;
myFunc = function(str: string, word: string){
    return str.indexOf(word) > -1
}
console.log(myFunc('lee','e')); // true

函数类型的类型检查,函数的参数名不需要与接口里定义的名字相匹配。

interface IncludeFunc{
    (str: string, word: string): boolean;
}
let myFunc: IncludeFunc;
myFunc = function(str1: string, word1: string){ // str1和word1不匹配亦可
    return str1.indexOf(word1) > -1
}
console.log(myFunc('lee','e')); // true

4.6 可索引的类型

interface strArr {
    [index: number]: string // 定义具有索引类型
}
let myStrArr: strArr;
myStrArr = ['l','e','e'];
let myStr: string = myStrArr[0];
console.log(myStr) // l

TS支持两种索引签名,字符串和数字,可以同时使用,数字索引的返回值必须是字符串索引返回值类型的子类型 字符串和数字会出现混淆的情况,因为使用时1会变为'1',即转换为string,发生意想不到的情况。两者必须保持一致。

class Animal {
    name: string;
}
class Dog extends Animal {
    breed: string;
}

// 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
interface NotOkay {
    [x: number]: Animal;
    [x: string]: Dog;
}
//报错 Numeric index type 'Animal' is not assignable to string index type 'Dog'.
class Animal {
    name: string;
}
class Dog extends Animal {
    breed: string;
}
interface NotOkay {
    [x: number]: Dog;
    [x: string]: Animal;
}
 // 不报错

可索引类型中的属性类型应当与索引类型保持一致。

interface NumberDictionary {
  [index: string]: number;
  length: number;    // 可以,length是number类型
  name: string       // 错误,`name`的类型与索引类型返回值的类型不匹配
}

可以给索引签名设置为只读,防止给索引值赋值

interface ReadonlyStrArr {
    readonly [index: number]: string;
}
let myArr: ReadonlyStrArr = ["lee", "zw"];
myArr[2] = 'lll'; // 报错 Index signature in type 'ReadonlyStrArr' only permits reading.

只读不能修改

4.7 类类型

TS强制一个类去符合某种契约。

interface ClockInterface {
    currentTime: Date;
}

class Clock implements ClockInterface {
    currentTime: Date;
    constructor(h: number, m: number) { this.currentTime = new Date()}
}
let myclock = new Clock(1,1)
console.log(myclock.currentTime) // 2021-03-08T08:25:48.075Z

方法契合

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}
let myclock = new Clock(1,1)
myclock.setTime(new Date())
console.log(myclock.currentTime)

复杂的例子

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface; // 执行new后返回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);

4.8 继承接口

接口可以互相继承。

interface Person {
    name: string;
}

interface Student extends Person { // 继承了接口
    NO: number;
}

let student = <Student>{};
student.name = "lee";
student.NO = 10;

继承多个接口

interface Person {
    name: string;
}
interface Shape {
    height: number;
}

interface Student extends Person,Shape {
    NO: number;
}

let student = <Student>{};
student.name = "lee";
student.NO = 10;
student.height = 178;

4.9 混合类型

一个对象可以同时做为函数和对象使用,并带有额外的属性。

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;

4.10 接口继承类

当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
    select() { }
}

class Location {

}

5 类

5.1 类

class SayHi {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    greet() {
        return "Hi, " + this.name;
    }
}

let mySayHi = new SayHi("lee");
console.log(mySayHi.greet()) // Hi, lee

5.2 继承

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

class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}

const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();

通过extend继承

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); // 执行父类方法
    }
}

需继承的类有构造函数时,子类应当在其构造函数中执行super函数,子类方法需要使用父类方法时通过super调用

5.3 公共,私有与受保护的修饰符

5.3.1 public

TS里成员都默认为public

class Animal {
    public name: string; // 相当于都加上public
    public constructor(theName: string) { this.name = theName; }
    public move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

5.3.2 理解private

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // 报错 Property 'name' is private and only accessible within class 'Animal'.

只能内部方法使用

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
    sayName(){
        console.log(this.name)
    }
}

new Animal('dog').sayName(); // dog

TypeScript使用的是结构性类型系统。 当我们比较两种不同的类型时,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。

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; // 错误: Animal 与 Employee 不兼容.

因为 Animal和 Rhino共享了来自 Animal里的私有成员定义 private name: string,因此它们是兼容的。 然而 Employee却不是这样。当把 Employee赋值给 Animal的时候,得到一个错误,说它们的类型不兼容。 尽管 Employee里也有一个私有成员 name,但它明显不是 Animal里面定义的那个。 子类无法访问父类的私有变量,即派生类中无法访问

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
    constructor() { super("Rhino"); }
    getName(){console.log(this.name)}
}

let rhino = new Rhino();
rhino.getName(); // 报错 Property 'name' is private and only accessible within class 'Animal'.
rhino.name; // 报错

5.3.3 理解protected

protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。

class Animal {
    protected name: string;
    constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
    constructor() { super("Rhino"); }
    getName(){console.log(this.name)}
}

let rhino = new Rhino();
rhino.getName();  // Rhino (派生类可以访问protected属性,但不能访问private属性)
console.log(rhino.name) // Property 'name' is protected and only accessible within class 'Animal' and its subclasses.

构造函数被标记为protected后,意味着这个类不能被直接实例化,但是可以子类super执行而实例化,能被继承。

class Animal {
    protected name: string;
    protected constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
    constructor(name) { super(name); }
    getName(){console.log(this.name)}
}

let rhino = new Rhino('rihio'); // 继承后再实例化
let animal = new Animal('dog'); // 不能直接实例化

5.4 readonly修饰符

class Person {
    readonly name: string;
    constructor(name){this.name = name}
}
let dad = new Person('lee');
dad.name = "li"; // 报错 Cannot assign to 'name' because it is a constant or a read-only property.

参数属性

class Person {
    constructor(readonly name: string){}
}
let dad = new Person('lee');
console.log(dad) // Person { name: 'lee' }
dad.name = "li"; // 报错 Cannot assign to 'name' because it is a constant or a read-only property.

赋值和属性进行简略书写。

5.5 存取器

TS支持通过getter/setter来截取对象成员的访问

class Person {
    private _fullName: string;
    get fullName():string{ return this._fullName;}
    set fullName(newName: string){ 
        if(newName){
            this._fullName = newName
        }else{
            console.log('名字不合法')
        }
    }
}
let person = new Person();
person.fullName = ''; // 名字不合法
// 警告 Accessors are only available when targeting ECMAScript 5 and higher.

存取器要求你将编译器设置为输出ECMAScript 5或更高。 不支持降级到ECMAScript 3。

5.6 静态属性

class Person {
    static isBreathe = true;
    IsPersonAlive(){
        console.log(Person.isBreathe) // 静态属性通过类名访问
    }

}
let person = new Person();
person.IsPersonAlive(); // true

静态属性通过类名访问

5.7 抽象类

抽象类可以包含成员的实现细节

abstract class Person {
    constructor(public name: string){}
    abstract laugh(): void; // 必须在派生类中实现
    move():viod{
        console.log('moving...')
    }
}
let person = new Person('lee'); // 报错 Cannot create an instance of the abstract class 'Person'.

直接实例化报错,需实例化子类

abstract class Person {
    constructor(public name: string){}
    abstract laugh(): void; // 必须在派生类中实现
    move():void{
        console.log('moving...')
    }
}
class Student extends Person {
    constructor(name:string){
        super(name)
    }
    laugh(){ // 在子类上实现了
        console.log('hahahahaha')
    }
}
let student = new Student('lee')
console.log(student) // Student { name: 'lee' }
student.move(); // moving...
student.laugh(); // hahahahaha

5.8高级技巧

class Person {
    name: string;
    constructor(name){this.name = name}
}
let person:Person;
person = new Person('lee')

let person:Person;定义实例类型 把类当作接口

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

6 函数

TS为js函数添加了额外的功能。 可以使用含名字的函数和匿名函数。

// 命名函数
function add (a,b){
    return a + b;
}
// 匿名函数
let myAdd = function(a,b)
    return a+ b;
}

可以使用函数体外变量

let z = 100;

function addToZ(x, y) {
    return x + y + z;
}

6.1 函数类型

6.1.1函数定义类型

function add(x: number, y: number): number {
    return x + y;
}

let myAdd = function(x: number, y: number): number { return x + y; };

6.1.2 书写完整函数类型

(x: number, y: number) => number,保证返回值为number类型

let myAdd: (x: number, y: number) => number =
    function(x: number, y: number): number { return x + y; };
console.log(myAdd(1,1)) // 2

前后参数名字可以不同

let myAdd: (x1: number, y1: number) => number =
    function(x: number, y: number): number { return x + y; };
console.log(myAdd(1,1)) // 2

6.1.3 推断类型

let myAdd = function(x: number, y: number): number { return x + y; };
console.log(myAdd(1,1))  // 2

6.2 可选参数和默认参数

TS里每一个函数参数都是必须的,不传报错,多传也报错

function func(a:string,b:string):void{

}
func('lee','ll')
func() // 报错 Expected 2 arguments, but got 0.
func('lee','','') // 报错 Expected 2 arguments, but got 3.

设置可选参数,可选参数放置在后面

function func(a:string,b?:string):string{
    return a + (b ? b :'')
}
console.log(func('lee')) // lee
console.log(func('lee','zw')) // leezw
console.log(func('lee','zw','')) // 报错  Expected 1-2 arguments, but got 3.

TS里设置默认值,当用户没有传递这个参数或者传递参数值是undefined时,采用默认值。

function func(a:string,b='zw'):string{
    return a + b
}
console.log(func('lee')) // leezw
console.log(func('lee','zh')) // leezh
console.log(func('lee','zw','')) // 报错  Expected 1-2 arguments, but got 3.

可选参数与末尾的默认参数共享参数类型。例如上面两例(a: string, b?: string) => string,默认参数不需要放在最后面,可选参数必须放最后面。

function func(a='lee',b:string):string{ // 不需要放在最后面
    return a + b
}

6.3 剩余参数

TS你可以把所有参数集合到一个变量里。

function func(a: string,...rest:string[]):void{
    console.log(a,rest)
}
func('l','e','e') // 'l' ['e','e']

赋值给其他变量

function func(a: string,...rest:string[]):void{
    console.log(a,rest)
}
let myfunc: (a1: string,...rest1:string[])=>void = func;
myfunc('l','e','e')

6.4 this

6.4.1 this和箭头函数

this的值在函数调用时才确定。

let a = 2;
let obj = {
    a: 1,
    func1:function(){
       console.log(this.a) 
    },
    func2: function(){
        return function(){
            console.log(this.a)
        }
    }
}
obj.func1(); // 1
let func2 = obj.func2();
func2(); // undefined(严格模式,非严格模式返回2)

obj.func1()对象调用时this指向obj;func2调用时相当于函数调用,指向全局window; 如果想要func2指向obj,需要使用箭头函数

let a = 2;
let obj = {
    a: 1,
    func1:function(){
       console.log(this.a) 
    },
    func2: function(){
        return ()=>{
            console.log(this.a)
        }
    }
}
obj.func1(); // 1
let func2 = obj.func2();
func2(); // 1

箭头函数的this指向创建时的this值

小恐龙

专注于WEB和移动前端开发

5年以上经验业余讲师被访问基金爱好者
社交帐号