likes
comments
collection
share

一篇文章带你了解Typescript那些事儿

作者站长头像
站长
· 阅读数 21

前言

最近小凌在维护前同事写的代码时,因为旧代码缺少注释常常不知道该输入什么类型。心想着,如果一开始就用Typescript去设计代码,那么后续代码可维护性将会大大提高。

Typescript基于JavaScript之上的编程语言它解决了JavaScript自身类型系统的不足。

为什么要使用TypeScript

JavaScript根本没有任何的类型限制,用一个词来形容就是“任性”

这样一来就缺失了类型的可靠性。在使用一个变量时我们总要担心它是不是我们想要的类型。这样一来这个语言给人带来的感觉就是“不靠谱”

举个例子: 因为没有参数限制,原来需要输入两个number类型参数的函数,在输入string格式后功能就完全发生了改变。

一篇文章带你了解Typescript那些事儿

可能在注释时认真,看的认真也许就不会有问题。但是俗话说的好"君子约定有隐患,强制要求有保障",而使用Typescript就可以完全解决这个问题,大大提高了代码的可靠程度

安装与配置

安装

使用npm或者yarn安装

npm

npm init
npm install -g typescript

yarn

yarn init
yarn add typescript

module中存在相关js包时说明安装成功。

一篇文章带你了解Typescript那些事儿

配置

初始化配置

yarn tsc init

当根目录下出现tsconfig.json时说明初始化配置成功。

一篇文章带你了解Typescript那些事儿

以下是重要配置的解释:

一篇文章带你了解Typescript那些事儿

严格模式包括:

规则解释
noImplicitAny不允许变量或者函数参数具有隐式any类型
noImplicitThis不允许this上下文隐式定义
strictNullChecks不允许出现null或者undefined的可能性
strictPropertyInitialization验证构造函数内部初始化前后已定义的属性
strictFunctionTypes对函数参数进行严格逆变比较

类型

基础类型

Typescript的基础类型包括:

数据类型关键字
任意类型any
数字类型number
字符串类型string
布尔类型boolean
数组类型
元组
枚举enum
voidvoid
nullnull
undefinedundefined
nevernever

Object类型

Typescript中的Object类型并不单只对象类型,而是泛指所有的非原始类型也就是 对象数组元素

// 报错 “sex”不在类型“{ name: string; age: number; }”中。
const foo: {name:string,age:number} = {name:"铁子",age:18,sex:'男'}

数组类型

声明数组的方式有两种

第一种是使用 Array泛型

第二种方式就是用元素类型加方括号的形式声明一个数组类型

const arr1:Array<number> = [1,2,3]
const arr2:number[] = [1,2,3]

元组类型

元组类型是一种特殊的数据结构,就是明确元素数量和每个元素类型的数组。

const tuple:[number,string] = [18,'lwh']

一般用来一个函数当中去返回多个返回值,比如在react useState() 和ES2017中提供的 Object.entries(获取一个对象当中的键值数组),他的返回参数就是一个元组。

一篇文章带你了解Typescript那些事儿

枚举类型

元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。

enum typeDict {
    // 1 = '视频' // 枚举成员不能具有数值名。
    video = '视频',
    mp3 = '音频',
    link = '外链'
}

const course = {
    title:"喜马拉雅课程A",
    content:"学了都说好",
    type: typeDict.video
    // video 视频 mp3 音频 link 外链 
}

函数类型

当我们需要一个可选参数时我们也可以使用?的形式。

如果我们需要给参数进行默认值设置可以参考ES6的默认参数

如果我们不确定参数个数,我们也可以参考ES6的rest操作符

一篇文章带你了解Typescript那些事儿

任意类型any

由于JavaScript为弱类型的关系,它本身很多方法接收弱类型的参数。因为Typescript是基于JavaScript之上的,我们在使用过程中难免需要一个变量接收任意类型的参数。

一篇文章带你了解Typescript那些事儿

注意:any在JavaScript中主要用来兼容老的代码。

类型的隐式判断

在Typescript当中,如果我们没有明确通过类型注解去标记一个变量的类型,那typescript会根据你使用的状况去推断你的类型。

一篇文章带你了解Typescript那些事儿

如果我们声明变量时我们并没有给它赋值,那么Typescript可以把他隐式推断为any,之后我们可以将其赋值为任意的值。

一篇文章带你了解Typescript那些事儿

类型的断言

在某些特殊的情况下Typescript不能推断出某些变量的具体类型,而我们作为开发者,我们明确程序的执行状况明确知道变量是什么类型的。

一篇文章带你了解Typescript那些事儿

查看以上代码我们可以知道上述代码的find可能找到的是undefined或是number,根据程序具体逻辑我们可以知道返回的res一定是一个number类型。

实现断言的方式有两种:

第一种是使用 as (推荐第一种)。

第二种是在变量使用尖括号的形式 (这一种会和jsx的标签冲突)。

// 是一个明确接口返回的数字类型的数组
const nums = [111,222,111]

const res = nums.find(i => i > 1000)

// 对象可能未定义
// let square:number = res + res

// as 实现
const numas = res as number


const numasSum:number = numas * numas
// 尖括号方式实现
const numjian:number = <number>res 

const numJianSum = numjian * numjian

类与接口

接口

接口,可以理解为一种规范或者是一种契约。它是一种抽象的概念,它可以用来约定我们对象的结构。我们要使用一个接口就必须要遵循这个接口的全部约定

一篇文章带你了解Typescript那些事儿

比如说我们需要实现一个打印课程信息的方法,需要必须的参数titlecontentpic,若形参实现了接口那么我们的实参必须要有接口中的属性。

接口补充

对于接口中的成员还有一些特殊的用法。

// 课程接口
interface Course{
    title:string;
    remark:string;
    pic:string;
    workName?:string; // 可选成员
    readonly price:number; // 只读成员
}

我们首先来看可选成员,当我们对象当中的一个成员是可有可无的,一般课程的作业是可有无的。那么我们可以通过workName变量名后面的问号?来表示可选成员。 就等于标记workName的类型为stringundefined

一篇文章带你了解Typescript那些事儿

然后我们来看只读成员,一般我们课程的价格是不允许外界设置的,那么我们就在价格变量前面添加一个 readonly。属性只能再初始化时设置一次之后就无法修改

一篇文章带你了解Typescript那些事儿

最后来看动态成员的用法,我们接口再定义的时候不知道会有那些具体的成员。我们就可以使用动态成员的方式。只不过我们这边动态成员的类型是string,若赋值其他类型则会报错。

类 (编程中重要的概念)

类可以说是面向对象编程中最重要的概念,关于类的作用我们这边来简单描述一下,主要是描述一类事务的抽象特征

ES6之前,实现类的方式是函数+原型模拟实现

ES6开始JavaScript就有了专门的class

而Typescript对与ES6提供的之外还进行了额外的拓展

声明一个

class Person {
    name: string; // init name
    public readonly age: number;  // = 15
    protected  gender: boolean; // 
    constructor(name:string,age:number){
        this.name = name
        this.age = age
        this.gender = false
    }
    /**
     * 说一句话
     * @param msg  信息 
     */
    sayHi(msg:string):void{
        console.log(msg);
    }
}

我们声明一个,并建立一个构造函数,在这里我们依然可以对构造函数的参数进行类型指定类型。 值得一提的是,在Typescript中我们的属性必须要有初始值,我们可以在声明时赋值或者在构造函数中赋值即可。

访问修饰符

接下来我们来看访问修饰符,我们在之前声明的类中稍加修饰,其实其中的变量方法都可以加访问修饰符

首先我们来看private,使用private修饰的成员只能在类内部使用,在外部调用时就会报错,以下代码我们通过private修饰age属性在类实例化的时候我们就无法访问到内部的age变量。

一篇文章带你了解Typescript那些事儿

我们可以使用public来修饰类中的成员,使其可以公有访问,不过Typescript默认的成员修饰符就是public

一篇文章带你了解Typescript那些事儿

还有一个叫protected(受保护的) 的修饰符,我们可以新声明一个成员去使用它,我们发现在对象实例的时候依然无法使用protected修饰的成员,它主要是用来被子类所使用的。我们可以声明一个student类来继承person类,我们可以发现在student类内部是可以使用gender成员的。

一篇文章带你了解Typescript那些事儿

总结就是:

public公开的,都可以访问( 默认的修饰符)。

private私有的,只允许在类的内部使用。

protected受保护的,只有子类和自己可以使用。

只读属性

除了使用访问修饰符来控制我们的访问级别,我们还可以通过readonly的关键词来把我们的成员设置成只读的

一篇文章带你了解Typescript那些事儿

类与接口

可以通过implements关键字来实现接口

一篇文章带你了解Typescript那些事儿

以上是实现的两个类,,他们都有同名的方法,但实现的方式不一样。这个时候我们就可以理解为他们实现了同样的接口也就是协议

有同学就要说了,我们只要有一个同样的动物父类不就行了么?但世界上的动物并不是同样的进食方式和运动方式,我们需要有一个约束来使其必须有该方式并且有自己的实现方式。

抽象类

抽象类接口类似,用来去约束子类当中必须要有某些成员,但不同的是抽象类可以包含一些具体的实现而接口只能是一个成员的抽象它不包含具体的实现

// 抽象类只可继承
abstract class Animal {
    eat(): void{
        console.log('可以吃')
    }
    abstract run() :void; // 抽象方法必须实现 
}

class Dog extends Animal{

    run(): void {
        throw new Error("Method not implemented.")
    }

}

let dog = new Dog();
dog.eat(); // 直接调用抽象类的方法 可以吃

重写和重载

重写

// 重写
class Animal {
    public eat() {
        console.log("这是一个吃的方法")
    }
}
class Dog extends Animal {
    public eat() {
        console.log("这是一个小狗吃的方法")
    }
}
let dog: Dog = new Dog()
dog.eat()

发生在父类与子类之间。

方法名参数列表返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同

访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>private)。

重载

一篇文章带你了解Typescript那些事儿

在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载

泛型

泛型,就是指我们在定义函数接口或者类的时候我们没有定义具体的类型,等到我们使用的时候再去指定具体类型的这种特征

泛型的作用主要是极大的复用我们的代码。

/**
 * 创建列表
 * @param length 
 * @param value 
 * @returns 
 */
function crateList<T>(length:number,value:T):T[]{
    const arr = Array<T>(length).fill(value)
    return arr
}

const arrString = crateList<string>(3,'xx'); // ['xx','xx','xx']
const arrNumber = crateList<number>(3,15); // [15,15,15]

这里我们可以通过一个例子来体现我们泛型的作用。我们需要创建一个被填满数字的数组,我们就需要上面方法的代码 因为Array默认生成的any类型的数组,我们需要通过<T>指定它的类型,当我们要创建String类型数组时我们就需要复制一便再去改变类型。

类型的声明

在实际的开发中我们难免会用到一些第三方的NPM模块,这些NPM模块不一定是用Typescript,所以也不一定有强类型的规范。

比如我们下载比较流行的lodash模块,这个模块呢提供了比较多的函数。我们调用camelCase这个函数,这个函数的作用就是将我们的字符串转换为驼峰格式。参数为string 返回也是string当我们调用它的时候并没有什么类型提示。

一篇文章带你了解Typescript那些事儿

我们可以npm通过安装对应的类型声明

yarn add @types/lodash

安装完成后就会有相应的类型错误提示了:

一篇文章带你了解Typescript那些事儿

中文提示安装

Typescript是支持多语言化的,默认会根据你的操作系统和开发工具的语言错误信息。

我们可以通过 yarn tsc --locale zh-CN 来使其控制台支持中文的提示

yarn tsc --locale zh-CN

一篇文章带你了解Typescript那些事儿

对于编译器的提示我们可以使用 文件 =》首选项 =》 typeScript locale =》 设置为 zh-CN 即可

一篇文章带你了解Typescript那些事儿

一篇文章带你了解Typescript那些事儿

最后要说的

随着最后知识点的讲完我们今天的分享也到了尾声。纸上得来终觉浅,相信大家在实际运用中一定会有更深刻的见解。在后续使用的发现也会在这篇文章进行更新,欢迎大家收藏关注

以下是项目地址:ts项目地址

转载自:https://juejin.cn/post/7045710643390857224
评论
请登录