typescript入门笔记 | 青训营笔记

59 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

一、ts 开发环境安装

用 nodejs 安装 ts 的开发环境: npm i -g typescriptnpm i -g ts-node 编写 ts 文件后执行tsc即可编译 ts 文件为 js 文件。通过tsc -init可生成配置文件。

二、ts 中的类型声明

let 变量: 类型;
let 变量: 类型 = 值;
function fn(参数: 类型,参数: 类型): 返回类型{
  ...
}

若值类型与变量类型不相同,则会报错。 与 js 相比,ts 增加了为变量声明类型的功能,在开发中可以避免很多麻烦。

三、ts 中的类型

类型描述
number1,-1,22数字
string'hello',"hello",`hello`字符串
booleantrue,false布尔值
字面量本身变量值就是限制类型
any*任意类型
unknown*类型安全的 any
void空值(undefined)无值
never没有值不为任何值
object{age:20}任意 js 对象
array[1,2,3]任意 js 数组
tuple[1,2]元素,定长数组
enumenum{A,B}枚举
//字面量类型声明
let a: 10;
a = 10; //正确,可赋值为10
a = 20; //报错,只能为10

let b: "male" | "female"; // b只能等于"male"或"female"
b = "male";  //正确
b = "female" //正确
b = "hello" //报错
let c: number | string;  //c只能为number类型和string类型

let d: any; //关闭类型检查,不建议使用
//声明变量若不指定类型,则自动判断any
//any类型变量可赋值给任何变量

let e: unknown = "hi";
//unknown类型可以被赋予任何值,但不能赋值给任何变量

let f:string = "hello";
f = e as string;  //类型断言,可以让unknown类型变量赋值给相应变量

function fun():never{  //永远不会返回结果
    //一般用于抛出异常
    throw new Error('出错');
}

let g: {name:string,age?:age};
//属性名后加冒号代表属性可选。

let h: {name:string,[propName:string]?:any};
//[propName:string]?:any表示任意类型属性,且属性名为string,值可选

let i:(a: number,b: string)=>boolean;
//表示i为一个函数,且形参一为number类型,形参二为string类型,返回值为boolean类型

enum Gender{
    Male = 1,
    Female = 2
}
let j :{name:string,gender:Gender};
j={
    name:"张三",
    gender:Gender.Male  // 1
}

四、ts 的类

class Person {
  readonly name: string = "张三"; //readonly只读属性
  static age: number = 20; //static静态属性
  static hello() {
    //lei'fan
  }
}
const p = new Person();
p.hello(); //调用静态方法可以直接 类.方法名

五、ts 的构造函数和 this

创建构造函数

class Dog {
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
const dog = new Dog("狗1", 2);

this 代表 Dog 的每个实例对象,对象是谁 this 就是谁。

六、继承

class Animal {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  call() {
    console.log("call");
  }
}
class Dog extends Animal {
  run() {
    console.log("狗跑");
  }
  call() {
    console.log("狗叫");
  }
}
class Cat extends Animal {
  run() {
    console.log("猫跑");
  }
  call() {
    console.log("猫叫");
  }
}

继承将多个类中共有的代码写道一个父类中,供其他子类继承。

七、super 关键字

super 用于在子类中调用父类的方法或属性。

class Cat extends Animal {
  run() {
    console.log("猫跑");
  }
  call() {
    super.call(); //输出call,调用的是父类方法
  }
}

在子类构造函数中默认会调用 super(),并传入值。

class Cat extends Animal {
  constructor(name: string, age: number) {
    super(name, age);
  }
  run() {
    console.log("猫跑");
  }
  call() {
    super.call(); //输出call,调用的是父类方法
  }
}

八、抽象类

抽象类以 abstract 开头,不能用于创建对象,只能用于继承。

abstract class Animal {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  abstract call(); //抽象方法,没有方法体,只能定义在抽象类中,子类必须进行重写。
}

九、接口

interface MyCode{     //定义一个类中应该包含哪些属性和方法
    name: string;
    age: number;
    hello():string;
}
interface MyCode{    //可重复定义
    hobby: string;
}
const obj: MyCode={  //将同名接口中的属性与方法都要定义
    name:'张三';
    age:20;
    hobby:'打球';
}
interface MyCode {
  //定义一个类中应该包含哪些属性和方法
  name: string;
  hello(): string;
}
//实现接口
class MyClass implements MyCode {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  hello(): string {
    return "hello";
  }
}
  • 接口可重复定义,但实际效果是将所有同名接口合为一个接口。

  • 接口可以限制类的结构

  • 接口中所有的属性没有实际的值

  • 接口只定义对象的解构卖不考录实际值

十、属性的封装

class Cat {
  private _name: string;
  private _age: number;
  constructor(name: string, age: number) {
    this._name = name;
    this._age = age;
  }
  get name(): string {
    return this._name;
  }
  set name(value: string) {
    this._name = value;
  }
  get age(): number {
    return this._age;
  }
  set age(value: number) {
    this._age = value;
  }
}
  • publich 可在任意位置访问(默认值)
  • private 私有属性,私有属性只能在类内部访问
  • protected 受保护的属性,只能在当前类以及其子类中进行访问

十一、泛型

在定义函数或类时,如果遇到类型不明确就可以使用泛型

function fn<T>(a: T): T {
  return a;
}
fn(10); //不指定泛型
fn<string>("hello"); //指定泛型
function fn2<T, K>(a: T, b: K): T {
  return a;
}
fn2(123, "123");
fn2<string>(123, "123");
interface Inter{
    length: number;
}
function fn3<T extend Inter>(a:T):number{  //T必须是Inter的子类
    return a.length;
}
fn3('123');

十二、联合/交叉类型

  • 联合类型: IA | IB;联合类型表示一个值可以是几种类型之一。
  • 交叉类型:IA & IB ; 多种类型叠加到一起成为一种类型,他包含了所需的所有类型的特性。
type IBookList = Array<{
    author: string;
} & ({
    type: 'history';
    range: string;
} | {
    type: 'story';
    theme: string;
})>

类型保护与类型守卫

interface IA {a: 1, a1: 2}
interface IB {b: 1, b1: 2}
function log(arg: IA | IB){
    // 程序会报错
    // 访问联合类型时,处于程序安全,仅能访问联合类型中的交集部分
    if(arg.a){
        console.log(arg.a1)
    }else{
        console.log(arg.b1)
    }
}

由于IB中不存在a属性,进行判断时,arg不一定是IA,故arg为IB时程序会产生异常。

修改后:

function getIsIA(arg: IA | IB): arg is IA{
    // 类型守卫:定义一个函数,返回值为一个类型谓词,生效范围为子作用域
    return !!(arg as IA).a
}
function log2(arg: IA | IB){
    if(getIsIA(arg)){
        console.log(arg.a1)
    }else{
        console.log(arg.b1)
    }
}

通过getIsIA判断是否为IA,是则输出arg.a1,否则arg为IB,输出arg.b1。