vlambda博客
学习文章列表

大前端进击之路|TypeScript入门基础

语言类型


类型安全

  • 强类型

在语言层面限制函数的实参类型必须与形参类型相同,不允许任意的隐式类型转换。

  • 弱类型

弱类型不限制实参的类型,允许任意的隐式类型转换。

funciton sum(a, b) { console.log(a + b)}sum(1,2) // 打印 3sum(1,'a') //打印 1a

类型系统

  • 静态类型

一个变量声明时,它的类型就是明确的,声明过后,它的类型不允许再被修改。

  • 动态类型

变量的类型随时可以改变,运行阶段才能明确变量类型,变量没有类型,变量存放的值是有类型的。

var a a = 'hello'console.log(a) // helloa = 123console.log(a) // 123

JavaScript自有类型系统的问题

通过上面我们了解了强类型和弱类型、动态类型和静态类型,我们可以知道JavaScript语言是弱类型、动态类型的。这是因为早期JavaScript编写的应用比较简单,没有编译环节。类似上面的例子我们写了一个两个数相加的sum函数,我们只是想要让两个数字相加,但是如果我们参数输入了字符串,得到的结果就会是字符串拼接。这就是弱类型的问题,让我们的代码不可靠,不能及时发现问题。

强类型的优势

  • 错误更早暴露

  • 编码更准确,代码更加智能

  • 重构更可靠

  • 减少不必要的类型判断

TypeScript基础

TypeScript是什么?

TypeScript是一种由微软开发的自由、开源的编程语言。它是JavaScript的一个超集,本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。任何一个JavaScript运行环境都支持它,TypeScript提供最新的和不断发展的JavaScript特性,功能更加强大,生态也更健全、更完善。

缺点:

  • 项目初期,使用TypeScript会增加成本。

  • 语言本身相对于JavaScript多出一些新的概念,增加学习成本。

但是TypeScript是渐进式的,我们会使用JavaScript的语法可直接上手TypeScript。

TypeScript初体验


安装TypeScript

npm install -g typescript

配置文件

生成配置文件tsconfig.json

tsc --init

// 在生成的配置文件中比较常用的配置选项// target => 编译目标为es5// lib => 标准库声明,这里我们引用了ES2015、DOM、ES2017// outDir => 编译后文件存放位置// rootDir => 文件根路径


编译TypeScript文件

// 如果我们配置好tsconfig.json文件后可直接使用tsc命令编译tsc
// 如果我们没有配置好tsconfig.json文件tsc xxx.ts// 会给我们生成xxx.js

作用域问题

在TypeScript中会默认文件中的成员会作为全局成员,如果多个文件中有多个相同成员就会出现冲突。

解决办法:

  1. 使用IIFE提供独立作用域

(function () { const a = 123}())

  1. 在当前文件使用export,也就是把当前文件变成一个模块,模块具有单独的作用域。

const a = 123export {}

基本数据类型


原始数据类型

基础类型分为string、number、boolean、null、undefined、Symbol、void。

const str:string = 'string'const num:number = 1const boo:boolean = trueconst n:null = nullconst u:undefined = undefinedconst s:Symbol = Symbol()const v:void = undefined

Object类型

Object是指除了原始数据类型以外的其它类型

const fun:object = function () {}
const obj:{name:string, age:number} = { name : 'xm', age : 25}

Array类型

// 1.可以在元素类型后面街上[],表示由此类型元素组成的一个数组let arr : number[] = [1,2,3]// 2.也可以使用数组泛型let arr : Array<number> = [1,2,3]

元祖Tuple

元祖类型表示一个规定好元素数量和类型的数组。

let arr : [string, number] = ['xm',20]

枚举Enum

使用枚举我们可以定义一些带名字的敞亮,可以清晰的表达意图或创建一组有区别的用例。分为数字枚举和字符串枚举。

数字枚举
enum Direction { Up = 1, Down, Left, Right}// Up的初始值为1,其余的成员会从1开始自动增长。// Down为2,Left为3,Right为4

字符串枚举

字符串枚举并没有自增长的行为,但是可以很好的序列化。

enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT",}

函数类型

我们可以为一个函数的返回值设定类型

function sum(a: number, b: number): number { return a + b}sum(1, 2)// 我们为sum函数的两个参数设定为number类型,返回结果也设定位number类型

可选参数和默认参数

在TypeScript中我们可以在参数名旁使用?实现可选参数的功能,可选参数和默认参数必须跟在必须在最后

// 可选参数function buildName(firstName: string, lastName?: string) { if (lastName) return firstName + " " + lastName; else return firstName;}
// 默认参数function buildName(firstName: string, lastName:string = "xm") { // ...}

任意类型any

任意类型我们也可以成它为弱类型,意思就是任意类型都可以。

let foo: any = 'string'foo = 100foo.bar()

隐式类型转换

在TypeScript中存在隐式类型转换,因此建议我们为每个变量都添加明确的类型。

let age = 18 // numberage = 'string' // 报错
let foo // 不声明类型foo = 100 // 默认为任意类型anyfoo = 'string' // 不报错

类型断言

类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构,没有运行时的影响,只是在编译阶段起作用。就好比我们告诉TypeScript,相信我,我知道这个变量的类型是什么,我知道自己正在干什么。

// 假定这个 nums 来自一个明确的接口const nums = [110, 120, 119, 112]const res = nums.find(i => i > 0)const square = res * res // 在这里会报错,因为TypeScript不确定res的类型是什么,// const res: number | undefined
// 使用类型断言 两种方式是一样的const num1 = res as numberconst num2 = <number>res // 这种方式在JSX下不能使用

接口interface

定义一些结构,我们去使用这个接口就必须去遵守这些接口所定义的结构。

interface Post { title: string content?: string // ?代表可选成员 readonlys summary: string // 只读属性}
function printPost (post: Post) { console.log(post.title) console.log(post.content)}
printPost({ title: 'Hello TypeScript', content: 'A javascript superset'// 可选 summary: 'hello' // 只读无法修改})
// 动态成员interface Cache { [prop: string]: string }
const cache: Cache = {}
cache.foo = 'value1'cache.bar = 'value2'

类class

class Person { // 类的属性需要使用的话,需要明确声明类型 name: string age: number  constructor (name: string, age: number) { this.name = name this.age = age } sayHi (msy: string): void { console.log(`我是${this.name},${msg}`) } }

类的访问修饰符

  • 默认为public公共属性

  • private是私有属性,外部无法访问

  • protected受保护的,与private很相似,外部也无法访问,但是在派生类中可以被访问

  • readonly只读属性

  • 构造函数也可以被设置为private、protected

class Person { private name: string protected readonly age: number  constructor (name: string, age: number) { this.name = name this.age = age } sayHi (msy: string): void { console.log(`我是${this.name},${msg}`) } }

类和接口

interface Eat { eat(foot: string): void}interface Run { run(distance: number): void}class Person implements Eat, Run { eat(food: string): void {
} run(distance: number): void {
}}class Animal implements Eat { eat(food: string): void {
}}

抽象类

抽象类做为其它派生类的基类使用。它们一般不会直接被实例化。不同于接口,抽象类可以包含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。

abstract class Animal { abstract makeSound(): void; move(): void { console.log('roaming the earch...'); } // 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现 abstract run (distance: number): void}

泛型

function createNumberArray(length: number, value: number): number[] { const arr = Array<number>(length).fill(value) return arr}// 上面这个函数只能创建数字类型的数组,我们要是想要创建一个字符串类型的数组,又得写一个函数,这时候我们可以使用泛型function createArray<T>(length: number, value: T): T[] { const arr = Array<T>(length).fill(value) return arr}const res = createNumberArray(3, 100)const numArr = createArray(3, 'you')const strArr = createArray(3, 100)

类型声明

import { camelCase } from 'lodash'
declare function camelCase (input: string): string
const res = camelCase('hello typed')
// 也可以使用npm install @types/lodash --dev 添加类型声明模块