你应该知道的Typescript | 青训营笔记

216 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第1天,因为本次涉及到笔者比较喜欢的知识点,因此写下这篇文章记录一下。

写在前面

JavaScript的动态类型语言, 而Typescript是弱类型语言,比如说在js里面数字可以直接和字符串(相当于执行了toString()函数)相加,ts不行。ts是js的超集,对于静态语言ts,可以大幅增加可读性可维护性,在编译阶段暴露大部分错误,因此,如今大型的前端项目都转换成ts进行编写。

基础数据类型

/* 字符串 */
const q = 'string'; => const q: string = 'string';
/* number */
const w = 1; => const w: number = 1;
/* boolen */
const h = boolen;
/* null */
const r = null;
/* undefined */
const t = undefined;

类型声明及约束

对象类型

下图演示对对象类型进行类型约束,对于interface对象前的首字母I,是我们约定俗称对象名称的首字母,用于区分普通对象。


const class = IClass = {
    id: 1,
    language: 'zh',
    comment: 'test'
}

interface IClass {
    /* 只读属性: 不可以在对象初始化外赋值 */
    readonly id: number;
    language: 'zh'|'en'|'other';
    /* 可选属性 */
    comment?: string;
    /* 任意书写: 约束所有对象属性都必须是该属性的子类型 */
    [key: string]: any;
}

当进行如下的赋值/初始化的时候会有下面结果

/* 报错,"id" 为只读属性 */
class.id = 111;

/* 成功 */

class.test = 'test';
/* 报错,缺少必须属性"id" */
const _class : IClass = {
    comment: '1111'
}

函数类型

函数也是js中常用到的,比如下面有一个函数

// javascript
function add(x, y){
    return x + y;
}
const mult = (x, y) => x * y;

改写成typescript则为

// typescript
function add(x: number , y: number){
    return x + y;
}
const mult = (x: number, y: number) => number = (x, y) => x*y;

即在对应变量名后面添加上数据类型,同时typescript也支持如下的interface定义


interface IMult {
    (x: number, y: number): number;
}

const mult: IMult = (x, y) => x*y;

函数重载

我们可以通过函数重栽判断数据类型,从而函数实现多种功能,如使用interface

interface IGetDate {
    (type: 'string', time?: string): string;
    (type: 'date', time?: string): Date;
    (type: 'string' | 'date', time?: string): string | Date;
}

const getDate : IGetDate = (type, timestramp) => {

    const date = new Date(timestramp);
    return type==='date' ? date.toString() : date
}

但是这样会报错

Type '(type: any, timestramp: any) => string | Date' is not assignable to type 'IGetDate'.\
Type 'string | Date' is not assignable to type 'string'.
Type 'Date' is not assignable to type 'string'.ts(2322)

因为ts会自动判断返回类型,当interface定义的范围小于实例类型时则会报错。这时候其实可以对实例对象修改,也可以对interface进行修改。

数组类型

// 类型+方块号表示
type IArr1 = number[];
//范型
type IArr2 = Array<string | number | Record<string, number>>;

补充类型和枚举类型

除了前面所提到的特征,ts还提供了丰富的补充类型,如枚举类型, TypeScript 在 ES 原有类型基础上加入枚举类型,使得在 TypeScript 中也可以给一组数值赋予名字,这样对开发者比较友好,可以理解枚举就是一个字典。枚举类型使用enum来定义:

具体可参考:juejin.cn/post/699831…

enum EnumExample {
    add = '+',
    mult = '*'
}

与此同时,ts支持范型,类似于c++中的模版template,如

type IGetRepeatArr = <T>(target: T) => T[];

其中范型的作用还有其它种运用实例,本质上还是通过其他方式来做类型约束。

高级类型

联合/交叉类型

  • 使用 IA | IB 表示其中的几种类型可选
  • 使用 IA & IB 多种类型联合一起,包含全部特征

类型守卫

在写js为了函数复用扩展时,可能会有遇到这样的问题

function foo(props)
{ 
    if(typeof(props) === 'string')
    {
        //true
    }else
    {
        //false
    }
}

那么在TS中同样也会遇到一样的问题,我们可能会这么写

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)
    }
}

但是会报错,因为类型IA|IB不存在公共属性"a",在类型联合时,程序属于安全状态,仅仅能访问联合类型的交集部分。那么可以怎么改呢?

interface IA { a:1, a1: 2}
interface IB { b:1, b1: 2}

function getIsIA(arg: IA|IB): arg is IA{
    return !!(arg as IA).a;
}

function log(arg: IA | IB)
{

    if(getIsIA(arg)){
        console.log(arg.a1)
    }
    else{
        console.log(arg.b1)
    }
}

这里使用了is限定生效范围,且返回值是一个类型谓词,这样即可以完成类型保护。

写在后面

还没有写到的部分有比如说ts的工程实践,这个可以使用webpack打包/检查,及通过TSC进行编译,因为这是基础的入门篇,因此暂时记录下这么多笔记。