轻松5天拿下TypeScript

46 阅读9分钟

初识TypeScript

TypeScript具有类型系统,是JavaScript的一个超集,扩展了JS的语法。因此,JS与TypeScript可以一起执行,TypeScript通过类型注解提供编译时的静态类型检查,仅编译特定的ts语法的代码段。

// 变量名:类型 = 值;
const a:number = 1;

TypeScript语法特性

  • Classes
  • 模块 Modules
  • 接口 Interfaces
  • 编译时类型检查 Compile time type checking
  • Arrow 函数

TypeScript安装

TypeScript有两种安装方法:

  • NPM 安装:npm install typescript -g
  • MSI安装包安装(包下载地址

安装完成,即可使用TypeScript编译器,名称叫tsc,可编译生成js文件,命令:tsc filename.ts

TypeScript学习

类型

TypeScript在变量后面添加:xx类型就可以规定数据、方法的类型。

基础类型

  • 字符串:string
  • 数字:number
  • 布尔:boolean
  • undefined:undefined
  • null:null
  • :void) - 函数没有返回值或者返回值为空(undefined)时
  • 任意类型:any) - 类型不确定时,就可以使用(Unknow类型差不多,比any安全)
  • 字面量:定义什么就只能使用什么(eg:let animal:'dog' 则animal只能赋值为dog)
  • 枚举enum
// 可手动设置编号
enum Color {Red = 1, Green, Blue};
let c: Color = Color.Green;

复杂类型

  • 数组array:(:number[] / 数组泛型形式 :Array[number]
  • 元组tuple:长度固定的数组(:[string, boolean]
  • 接口interface:能很方便的定义Object类型
    • 在 interface 属性中添加 可以省略
    • readonly 不可改变的,定义完后就不能修改(和 const 有点像:const 是针对变量, readonly 是针对属性)
    • 任意数量的其它属性:[propName:String]: any
interface Infos {
    name: string,
    age: number,
    readonly idCard: string, // 不可修改
    male?: number, // 可省略
    [propName:String]: any, // 其它任意属性
}

let customer:Infos = {
    name: 'LeaT',
    age: 18,
    idCard: 'xxxxxx',
    address: 'sss'
}
  • 函数function
    • 规定函数的输入类型返回类型(传入多余参数会报错,可以为函数添加可选参数 即用 ? 即可,可选参数后不可再添加必须的形参
    • 形参后需要声明形参类型,函数名后需要声明返回值类型
function sumFun(num1:number, num2:number, num3?:number):number {
    return num1 + num2 + (num3 || 0)
}

// 写法二
const sunFun = (num1:number, num2:number, num3?:number):number => {
    return num1 + num2 + (num3 || 0)
}

定义函数类型变量

let mySum:(num1:number, num2:number, num3?:number) => number = sum

// 使用interface描述
interface sumObject {
    (num1:number, num2:number, num3?:number): number
}

let mySum:sumObject
// 使用时 可不指定类型
mySum = funtion(num1, num2, num3?) {
    return num1 + num2 + num3 || 0
}
  • 联合类型union types(避免any类型的使用)

    • 几种类型使用 | 分隔
    • 在没有赋值之前,仅可访问共同的方法、属性
  • 对象Object(简约处理interface的声明)

let obj:{width:number, height:number}
obj = {
    width: 12,
    height: 24
}

类型断言(type inference)

使用联合类型的时候,由于未赋值之前仅可使用公有的方法和属性。

但是在某些时候,你清楚地知道一个实体具有比它现有类型更确切的类型,通过类型断言方式可以告诉编译器,“相信我,我知道自己在干什么”。类型断言好比类型转换,但是不进行特殊的数据检查和解构,只在编译阶段起作用

写法

  • 尖括号:<string>param
  • as写法:param as string使用JSX时,仅as写法有效

类型守卫

使用联合类型的时候,需要针对某些类型做特殊返回或者处理,这个时候就需要用到typeofinstanceof等类型判断方法。

function getLen(params: number | string):number {
    if(typeof params === 'string') return params.length;
    return params.toString().length
}

泛型(T

泛型就像一个占位符一个变量,在使用的时候可以将定义好的类型像参数一样传入,原封不动的输出。

function getValue<T,U>(params:[T,U]):[T,U] {
    return params
}

在使用的时候,编译器就会自己判断并修改传入的类型。

image.png

在此,我们可以使用interface来约束泛型。

eg:约束传入的参数必须有length属性

interface needLen {
    length: number
}
function getLen<T extends needLen>(params:T):number {
    return params.length
}

// 使用
getLen('str') // 3
getLen([]) // 0

getLen(124) // error

类中使用泛型

class Animal<T> {
    singing(name: T) {
        console.log(name)
    }
}

let kit = new Animal<string>()
kit.singing('lalala') // lalala

kit.singing(2342) // error

接口中使用泛型

class getValue<T, U> {
    key: T,
    value: U
}

let test1:getValue<string, number> = {key: 'test1', value: 1}
let test1:getValue<number, number> = {key: 2, value: 2}

数组中使用泛型

let array1:Array<number> = [1,2,3,4,5]
let array2:Array<string> = ['1','2','3']

类型别名(type

type numStrType = number | string

let num:numberType = 243

交叉类型(&

interface valueKey {
    key: string
}

test valType = valueKey & {value: string}

let testValue:valueType = {kay: 'test', value: 'val'}

类 class

每个类都有一个constructor属性,new时会执行该构造函数并初始化它。

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

let greeter = new Greeter("world");

参数修饰符

  • public:公共
  • privated:私有的,只能在本类访问
  • protected:受保护的,只能在本类子类中访问

静态属性(static

静态属性是用过访问的,是每个实例共有的,可以直接调用。

只读属性(readonly

即只能读取,不能修改。如果存在 static 修饰符,写在其后

class MyClass {
    static readonly species:string = 'haha'
}

抽象类(abstract

TypeScript新增的抽象类:使创建的类只能被继承,不能被实例化(即不能new

  • 在 class 前面 添加 abstract 修饰符;
  • 在抽象类中写抽象方法,抽象类没有方法体
abstract class Animal {
    constructor(name: string) {},
    abstract singing():void
}

class Cat extends Animal {
    constructor(name:string) {
        super(name)
    }
    singing() {
        console.log('miao~')
    }
}

继承 (extends

类最常见使用就是类的继承使用。使用extends关键字来基于基类创建子类,使其子类均可以访问基类的属性和方法。

  • constructor的派生类必须调用super(),用于执行基类的构造方法;
  • 类中的成员,默认都是公有的,即public声明;
  • private修饰成员变量后,则不能在声明它的类的外部访问;
  • protected修饰变量后,派生类中可以访问(构造函数也可以被标记为protected,表示这个类不能在包含它的类外被实例化);
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);

输出:

Slithering...
Sammy the Python moved 5m.
Galloping...
Tommy the Palomino moved 34m.
class Person {
    protected name: string;
    protected constructor(theName: string) { this.name = theName; }
}

// Employee can extend Person
class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name);
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee("Howard", "Sales");
let john = new Person("John"); // Error: The 'Person' constructor is protected

参数属性

参数属性可以方便地让我们在一个地方定义并初始化一个成员。 下面的例子是对之前Animal类的修改版,使用了参数属性:

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

// 等同于
name:string,
constructor (theName: string) {
    this.name = theName;
}

声明和赋值合并至一处:参数属性通过给构造函数参数添加一个访问限定符来声明,和在声明变量之前添加修饰符作用一致。

接口 (interface

接口的存在,是为了解决一个类只能继承另一个类,而不能实现多继承的困境。

通过实现一个接口,将某个共有的功能抽离出来,使得多个类去实现该接口即可。

我们通过使用关键字implements来实现接口。

interface getColor {
    generateCol():void
}

interface getSize {
    generateSize():void
}

// 通过,连接多个接口 实现多个接口功能
class getCircleItem implements getColor,getSize {
    generateCol(num) {
        return num * 2
    }
    generateSize() {
        console.log('size')
    }
}

接口可以继承,通过继承,可以细化和合并实现不同功能项

interface getColor {
    generateCol():void
}

interface getSize extends getColor {
    generateSize():void
}

// 通过,连接多个接口 实现多个接口功能
class getCircleItem implements getSize {
    generateCol(num) {
        return num * 2
    }
    generateSize() {
        console.log('size')
    }
}

TypeScript内置的类型方法和工具类型

工具类型

  • Partial<T>:将 T 类型的所有属性变为可选
  • Required<T>:将 T 类型的所有属性变成必须
  • Readonly<T>:将 T 类型的所有属性变成只读
  • Record<T,K>:创建一个具有指定键类型 K 和值类型 T 的新对象类型
  • Pick<T,K>:从类型 T 中选择指定属性 K 形成新的类型
  • Omit<T,K>:从类型 T 中排除指定属性 K 形成新的类型
  • Exclude<T,U>:从类型 T 中排除可以赋值给类型 U 的类型
  • Extract<T,U>:从类型T中提取可以赋值给类型U的类型
  • NonNullable<T>:从类型T中排除nullundefined类型
  • ReturnType<T>:获取函数类型T的返回类型
  • Parameters<T>:获取函数类型T的参数类型组成的元组类型

条件判定类型

条件类型是一种灵活的类型构造方式,它允许根据类型关系进行条件判断生成不同的类型。

条件类型

  1. 条件类型基于输入的类型关系来确定最终的类型,通常结合extends关键词来使用。
type TypeName<T> =
  T extends string ? "string" :
    T extends number ? "number" :
      T extends boolean ? "boolean" :
        "other";

type A = TypeName<string>;            // A 的类型为 "string"
type B = TypeName<number>;            // B 的类型为 "number"
type C = TypeName<boolean>;           // C 的类型为 "boolean"
type D = TypeName<object>;            // D 的类型为 "other"
type E = TypeName<string | number>;   // E 的类型为 "string" | "number"

  1. 条件类型中使用infer关键字,用于在条件类型内部声明一个类型变量,并从中提取或推断出一个类型,它允许我们在泛型条件类型中推断出待推断类型的部分。
  2. 条件类型配合泛型使用

分布式条件类型

分布式条件类型在联合类型上进行推断和分发,并返回联合类型中每个成员的条件类型。

type ToArray<T> = T extends any ? T[] : never;

type StrArray = ToArray<string>; // StrArray 的类型为 string[]
type NumArray = ToArray<number>; // NumArray 的类型为 number[]
type UnionArray = ToArray<string | number>; // UnionArray 的类型为 (string | number)[]

映射类型

TypeScript中的映射类型,允许通过已有类型来创建新类型,通常是通过映射现有类型的属性、方法或者创建新的属性来实现。

创建的映射类型是利用keyof关键字配合索引类型来生成新的类型。

type Partial<T> = {
    [P in keyof T]?: T[P];
};

interface User {
    name: string;
    age: number;
}

type PartialUser = Partial<User>;
// PartialUser 类型为 { name?: string; age?: number; }

模板文字类型

模板文字类型允许在类型系统中对字符串文本进行操作和替换。使用${}语法来动态的创建字符串字面量类型。

类型推断关键字

  • typeof:类型查询操作符,用户获取变量或表达式的类型,返回该值的类型字符串表示
  • instanceof:用于检查对象是否是特定类的实例,返回布尔值检查结果
  • in:检查对象是否具有特定属性;
  • type guards:类型守卫是自定义的函数或条件语句,用于在代码块内缩小变量的类型范围。它们可以是typeofinstanceof或者其它自定义条件的组合
  • as:用于类型断言,允许将一个变量断言为特定的类型。