Typescript入门 | 青训营笔记

94 阅读6分钟

本篇可算作TypeScript的一个学习路线和侧重点参考吧。 TypeScript入门小白表示,学习一门编程语言仅靠一篇笔记是远远不够的,还得多实践! 日后学习后再作补充吧,囫囵吞枣会消化不良🤯

why TS?

TS是由微软开发的开源编程语言,在JavaScript的基础上添加了静态类型定义构建。TS可以通过TS编译器或Babel转译为JavaScript代码,可运行在任何浏览器和操作系统。

TypeScript与JavaScript对比

TypeScriptJavaScript
是JavaScript的超集,用于解决大型项目代码复杂性的问题是一种脚本语言,用于创建动态网页
强类型支持静态和动态类型(any)动态弱类型语言
可以在编译期间发现并纠正错误只能在运行时发现错误
不允许改变变量的数据类型变量可以被赋值为不同类型

(TS具有JS的语法,但在数据类型等限制方面又具有Java、C#的影子)

TypeScript的特性

  • 类型安全(不能赋值任意类型)
  • 支持下一代JS特性(JS新特性,eg.类等)
  • 完善的工具链

GitHub学习资源:

TS到JS在线编译:TS Playground

TS基础

基础类型

  1. boolean、number、string、undefined、null

    number包含了整数、浮点数、负数和NaN等所有数字。

  2. enum 枚举:用于定义数值集合。枚举是TS中特有的类型

    enum Color {Red, Green, Blue};
    let c: Color = Color.Blue;
    console.log(c);    // 输出 2
    
    enum Fruit {
        apple,      // 0
        orange,     // 1
        banana,     // 2
    }
    
    enum Fruit2 {
        apple = 1,  // 1
        orange,     // 2
        banana = 100,  // 100
        pear,       // 101
    }
    
  3. any、unknown、void

    any可以赋值和反向赋值;unknown只能被赋值,反向赋值只能给any和unknown

  4. never:是其它类型(包括null、undefined)的子类型,代表永远不会出现的值

  5. []数组类型

    // 在元素类型后面加上[]
    let arr: number[] = [1, 2];
    
    // 或者使用数组泛型
    let arr: Array<number> = [1, 2];
    
  6. 元组类型:元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。

    let x: [string, number];
    x = ['Runoob', 1];    // 运行正常
    x = [1, 'Runoob'];    // 报错
    console.log(x[0]);    // 输出 Runoob
    

函数类型

TS定义函数类型时需要定义输入参数类型和输出类型。

  • 输入参数:支持可选参数和默认参数
  • 输出参数:输出可以自动推断,没有返回值时,默认为void类型
  • 函数重载:名称相同但参数不同(函数签名),可以通过重载支持多种类型。
function add(x: number[]): number
function add(x: string[]): string
function add(x: any[]): any {
    if (typeof x[0] === 'string') {
        return x.join()
    }
    if (typeof x[0] === 'number') {
        return x.reduce((acc, cur) => acc + cur)
    }
}

interface 接口

接口是为了定义对象类型。(类似于给定一个规范模板)灵活 duck typing

特点:

  • 可选属性:?
  • 只读属性: readonly
  • 必选属性: -
  • 可以描述函数类型
  • 可以描述自定义属性
interface Person {
    name: string
    age: number
}

const p1: Person = {
    name: 'pp',
    age: 18
}


interface RandomKey {
    [propName: string]: string
}

const obj: RandomKey = {
    a: 'hello',
    b: 'world'
}

写法和JS差不多,多了一些定义和修饰符

  • 修饰符:
    • public:默认
    • private:私有,继承类和实例都不可以调用
    • protected:继承类可用、实例不可用
class Person{
    protected name: string
    private sex: string
    public constructor(name: string){
        this.name = name
        this.sex = 'male'
    }
}  

class Student extends Person{
    study(){
        console.log( this.name)
        console.log( this.sex)  // ❌ Property 'sex' is private and only accessible within class 'Person'.
    }
}  

let person = new Person('zhangsan');
person.name  // ❌ Property 'name' is protected and only accessible within class 'Person' and its subclasses.
person.sex   // ❌ Property 'sex' is private and only accessible within class 'Person'.
  • 抽象类 abstract
    • 只能被继承,不能被实例化
    • 作为基类,抽象方法必须被子类实现
  • interface 约束类,使用implements关键字
abstract class Animal {
    public name: string
    public abstract sayHi():void    // 抽象方法
    constructor(name: string) {
        this.name = name;
    }
}

class Dog extends Animal{
    constructor(name:string){
        super(name);
    }
    // 若不实现 sayHi 方法,则会报错
    public sayHi(): void {
        console.log('hi');
    }
}

TS进阶

高级类型

  1. 联合类型 |:类型可以为指定类型中的任意一个
let num: number | string
num = 8
num = 'eight
  1. 交叉类型 &:可用于扩展数据类型(同名数据类型取交集、同名非基础类型会进行相关元素的组合)
interface Person{
    name:string
    age: number
}

type Student = Person & { grade: number}
// Student类型拥有两种类型中的属性
let stu:Student = {
    name: 'zs',
    age: 8,
    grade: 2
};
  1. 类型断言 as:告诉编译器某个变量的类型
function getLength(arg: number|string):number{
    const str = arg as string  // 告诉编译器此时arg为string类型。若不写,下一行会报错
    if(str.length){
        return str.length
    } else{
        const num = arg as number
        return num.toString().length
    }
}
  1. 类型别名 type:给类型起别名便于使用

type 和 interface

一般使用组合和交叉类型的时候使用type,涉及类(eg. extends、implements)时使用interface

  • 相同点
    • 都可以定义对象或函数
    • 都允许继承
  • 差异点
    • interface是TS用来定义对象的,type用于定义别名方便使用
    • type可以定义基本类型,interface不行
    • interface可以合并重复的声明,type不行

泛型

用于提高灵活性和重用性,使得组件不仅能够支持当前的数据类型,同时也能够支持未来的数据类型。

(泛:可以理解为广泛、不局限)

// 泛型能够将输入输出类型关联起来
function print<T>(arg: T): T {
    console.log(arg)
    return arg
}

泛型的基本定义

核心:引入了临时占位的类型,之后通过类型推导确定类型

基本定义:

  • 基本语法:<>里写类型参数,一般使用T表示;
  • 使用时指定类型的方法:
    • 定义要使用的类型
    • 通过TS类型推断,自动推导类型
    function print<T>(arg: T): T {
        console.log(arg)
        return arg
    }
    print<string>('hello')  // 定义要使用的类型
    print('hello')   // 通过TS类型推断,自动推导类型
    
  • 泛型的作用:临时占位,之后再根据传入的类型进行推导

泛型基础操作符

  • typeof:获取类型

  • keyof:获取所有键

  • in:遍历枚举类型

    type Keys = 'a' | 'b' | 'c'
    type Obj = {
       [p in Keys]: any
    }  // {'a':any, 'b':any, 'c':any}
    
  • T[K]:索引访问

    interface IPerson{
      name: string
      age: number
    }
    
    let type1: IPerson['name']  // string
    let type2: IPerson['age']   // number
    
  • extends:泛型约束

泛型常用工具类型

  • Partial<T>:将类型属性变为可选
  • Required<T>:将类型属性变为必选
  • Readonly<T>:将类型变为只读
  • Pick、Record……
type Partial<T> = {
    [p in keyof T]?: T[p]
}

type Required<T> = {
    [p in keyof T]-?: T[p]
}

type Readonly<T> = {
    readonly [p in keyof T]: T[p]
}

实战&工程向

声明文件

  • declare:第三方库需要类型声明文件。declare 定义的类型只会用于编译时的检查,编译结果中会被删除。
// 
declare var jQuery: (selector: string) => any;

jQuery('#foo');
  • .d.ts文件:声明文件定义

    在TS中直接调用引用其他第三方的 JavaScript 库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。为了解决这个问题,需要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生了一个描述 JavaScript 库和模块信息的声明文件。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了。

    // 声明文件或模块的语法格式
    declare module Module_Name {
    }
    
    // TypeScript 引入声明文件语法格式:
    /// <reference path = " runoob.d.ts" />
    
  • @types:第三方TS类型的包

  • tsconfig.json:编译TS相关配置

泛型约束后端接口类型

提前定义好规范,并将此规范告知编译器,提高了开发效率。 比如:接口定义后,在编码过程中就可以借助代码编辑器的提示快速发现诸如拼写错误等问题。 图片.png