Typescript基础语法|青训营笔记

102 阅读6分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 4 天,欢迎各位大佬批评指正。

Typescript 发展与现状

1. 为什么我们需要Typescript

相比于JavascriptTypescript是一门静态类型的语言,这使得我们在编程过程中就可以很规范得去对数据类型进行限定,并且依据其强大的类型推论和IDE结合,可以在编码过程及时对一些语法或者数据类型的错误进行提示。有着更好的可维护性和迭代性,保证大型开发项目的效率。

2. 前端主流框架对Typescript的支持

目前前端框架对Typescript都已经有支持~

  • Vue3中为了更好兼容ts,可以在setup语法糖中设置lang='ts';可以直接写ts语法。
  • React在jsx语法中可以直接使用ts语法,后期由babelreact翻译~

Typesscript 的基础语法

1. 基本数据类型的限定

基础数据类型直接在变量后面用冒号的形式进行注解~(一般用小写,大写一般用来表示类

let str: string = 'abc';
let num: number = 123;
let bol: boolean = true;
let syn: symbol = Symbol('a');
/*************************************/
let x: any = 1; //any类型指所有数据类型,当实在不清楚具体数据类型时可以指定,它会在tsc编译阶段移除检查
x = '1'; //因为是any类型,因此可以更改为字符串
x = true; //可以更改为布尔类型
/*************************************/
let y: undefined = undefined; //很少用
let z: null = null; //很少用

undefinednull是所有类型的子类型,可以赋值给所有类型。(注意:strictNullChecks标记要改为false,否则会报错)

2. 函数

函数的注解其实就是注解参数返回值(如果没有返回值,注解为void)~

  • 函数声明:函数声明直接在参数后面进行注解:
function add(a: number, b: number): number {
    return a + b;
}
  • 函数表达式:函数表达式就是在变量后面用冒号把函数的结构注释出来:
let print: (str: string) => void = function (str) {
    console.log(str);
}
  • 接口(interface)定义:interface 中可以定义函数:
// 定义函数
interface Plus {
    (a: number, b: number): number;
}
// 实现函数
function plus: Plus (a, b) {
    return a * b;
}
  • type定义:type定义和interface几乎一样:
// 定义函数
type Plus = (a: number, b: number): number;
// 实现函数
function plus: Plus (a, b) {
    return a * b;
}

此外,函数的参数可以有剩余参数(...arg: number[]),可选参数(?)等~

3. 类

对于类本身的注解其实就是注解类身上的属性和方法。ts相比于js而言,对类的三大特征(继承、封装、多态)有更好的支持。

  • 类的基本注解:

    类的注解就是注解自身的属性和方法,和前面介绍的基本类型注解函数类型注解一样:

class Person {
    public name: string;
    public constructor(name: string) {
        this.name = name;
    }
    public say(): string {
        return `My name is ${name}`;
    }
}
  • 类的三大修饰器

类身上的三大修饰器分别是:publicprivateprotected;用以修饰类身上的成员。

  • public 公有成员:类自身、子类和实例都可以访问。安全程度低。
  • private 私有成员:仅类自身可以访问。安全程度最高。
  • protected 保护成员:类自身、子类可以访问;实例不能访问。安全程度一般。
  • 只读属性与参数属性
  • 只读属性:readonly修饰只能访问不能修改的属性,写在三大修饰器之后。
  • 参数属性:当参数名和类自身的属性名一致的时候,可以进行简写,例如上面可以写成这样:
class Person {
    public constructor(public name: string) {
        this.name = name;
    }
    public say(): string {
        return `My name is ${name}`;
    }
}
  • 抽象类与类的继承

类也可以把一些共有的方法和属性抽象形成一个抽象类;其他子类可以继承抽象类。抽象类用关键字abstract,具体定义方法如下:

abstract class Car {
    public name: string;
    public color: string;
    public abstract switch: boolean;
    public constructor(name: string, color: string) {
        this.name = name;
        this.color = color;
    }
    abstract run(): void;
}

class BMW extends Car {
    switch = false;
    constructor(name: string, color: string) {
        super(name, color)
    };
    run() {
       this.switch = true;
    }
}

注:抽象类中抽象的方法和属性必须在继承类中实现~~~

4. 接口

接口有两大作用;其一是描述对象的形状,其二是对类的行为抽象

  • 描述对象形状:在一些特定的场景中,如果需要指定一个希望得到的对象,就需要对对象进行注解,通常是通过interface的方式描述对象的形状,即将对象的一些属性加上限制,用以注解对象。基础的interface如下:
// 定义对象的形状
interface Person {
    readonly id: number;
    name: string;
    age: number;
    sex?: string;
    [propName: string]: any
}
// 注解对象
let jake: Person = {
    id: 1,
    name: 'jake',
    age: 20,
    weight: '70Kg'
}

此外,在一些场景中如果需要先申明变量为空对象再通过点语法赋值时候的时候,可以用断言的方式固定变量的类型,这个时候ts内部不会去做类型推论。

interface Test {
    id: number;
    name: string
}

let test = {} as Test;
test.id = 1; //true
test.name = 'Tom'; //true
  • 对类的行为抽象

前面讲到接口可以定义函数和对象的属性;其实类的行为也就是类身上的方法和存在的属性。在这里就不过多介绍,值得注意的一点是,类在用接口注解的时候关键字用implements,意为实现的意思。并且接口中定义的方法和属性都必须在类中最终实现

interface DemoInterface {
    name: string;
    age: number;
    say(name: string, age: number): string
}

class DemoPerson implements DemoInterface {
    public name: string;
    public age: number;
    public constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
    public say(name: string, age: number) {
        return `My name is ${this.name} and I'm ${this.age} years old.`
    }
}

5. 泛型

在ts的使用场景中经常会有需要根据传入的变量类型决定返回的变量类型的情景。比如以下的例子,函数根据传入的参数类型决定返回值类型:

function ReturnSelf <T>(arg: T): T {
    return arg;
}

尖括号中的T代表的是类型变量,在函数使用传值的时候才会最终固定~

6. 元组/数组

  1. 数组

数组的注解方式有多种:

  • 简单定义:类型[]的形式
let arrNum: number[] = [1, 2, 3, 4];
let arrStr: string[] = ['Tom', 'Jack', 'Rose'];
let arrMix: (number | sring)[] = ['Zhangsan', 10, 20];
  • 接口方式定义:
interface Arr {
    [index: number]: number;
}
let arr: Arr = [1, 2, 3, 4, 5];
  • 泛型定义
let arr: Array<number> = [1, 2, 3, 4];
  1. 类数组

类数组定义和普通数组不一样,原因在于类数组身上没有数组的方法。因此在对类数组的注解要用到ts的内置对象:IArguments

function test(): void {
    let res: IArguments = arguments;
    console.log(res);
}
  1. 元组

元组是指的已知元素数量和类型的数组,各元素的类型不必相同,但是对应位置的元素类型必须相同。

let list: [number, string, number?] = [1, 'a'];

注:元组可以越界添加,但是不可以越界访问!

Typescript 高级用法

函数重载

函数重载是在ts中非常重要的一个概念。意义在于重载函数签名,注解更加清楚,同时也便于ts内部做类型推论。比如泛型中举的例子,除了用泛型的写法外,也可以使用函数重载的方式:

function ReturnSelf(arg: number): number; //重载函数签名
function ReturnSelf(arg: string): string; //重载函数签名
function ReturnSelf(arg: number|string): number|string {
    return arg;
}

相比于泛型,函数重载可以实现更高自由度的返回类型设定,比如传入字符串返回数字类型;泛型做不到这样~


更多ts高级用法会在后续学习中持续更新~~