TypeScript
一、概述
1、任何一种JS运行环境都支持:Web Node.js reactNative electric桌面应用
2、功能更为强大,生态也更健全、更完善
3、Angular/Vue.js 3.0
4、前端领域中的第二语言
缺点一:语言本身多了很多概念,如接口 泛型 枚举等
5、渐进式
缺点二:项目初期,TS会增加一些成本
二、快速上手
1、yarn init --yes
2、yarn add typescript --dev node_modules .bin目录下 多出一个tsc命令
3、完全可以按照JS的标准语法去编写代码,支持最新ES标准
4、编译 yarn tsc xxx.ts 然后会多出一个同名的js文件
5、vscode默认就支持对TS的语法类型检查,不用等到编译,在vscode中就直接看到类型错误
三、配置文件
1、tsc命令不仅仅可以编译指定的某个ts文件,还可以用来编译整个项目或者说整个工程
2、编译项目之前,先给这个项目创建个TS的配置文件,用命令自动生成配置文件
yarn tsc --init 新增tscconfig.json文件
文件中compilerOptions属性,这个属性就是typescript编译器所对应的一些配置选项
yarn tsc 编译命令
四、原始类型
target改为es5
// 原始数据类型
const a: string = 'foobar'
const b: number = 100 // NaN Infinity
const c: boolean = true // false
// 在非严格模式(strictNullChecks)下,
// string, number, boolean 都可以为空
// const d: string = null
// const d: number = null
// const d: boolean = null
const e: void = undefined // null 也可以 但是严格模式下只能是undefined,一般用在函数没有返回值的时候去标记返回值类型
const f: null = null
const g: undefined = undefined
// Symbol 是 ES2015 标准中定义的成员,
// 使用它的前提是必须确保有对应的 ES2015 标准库引用
// 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015
const h: symbol = Symbol() // 这边会报错,解决将target改成es2015
symbol就是js中内置的标准对象
// Promise
// const error: string = 100
如若必须编译到es5可以使用ts配置文件中的lib选项去指定所使用的标准库
“libs”:[‘2015’] 但是console对象又会报出错误。因为console对象在浏览器当中是bom所提供的,而lib中是这是了es2015,所以说所有默认的标准库都被覆盖掉了,TS中把BOM DOM都归结到一个标准库文件中就叫做DOM
“libs”:“2015”,“DOM”]
标准库就是内置对象所对应的声明文件,在代码中使用内置对象就必须要引用对应的标准库,否则TypeScript就找不到所对应的类型,就会报错
四、TypeScript中文错误信息
yarn tsc --locale zh-CN 这样就会显示中文的错误消息,
对于Vscode中的错误消息,可以在配置选项中去配置
settings–>搜索typescript locale 设置zh-CN 不建议使用
五、TS作用域问题
不同文件相同变量名称的情况
// 作用域问题
// 默认文件中的成员会作为全局成员
// 多个文件中有相同成员就会出现冲突
// const a = 123
// 解决办法1: IIFE 提供独立作用域
// (function () {
// const a = 123
// })()
// 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块
// 模块有单独的作用域
const a = 123
export {} // 这知识export的一个语法,并不是导出了一个空对象
六、TypeScript Object类型
泛指所有的非原始类型:对象、数组、函数
// Object 类型
export {} // 确保跟其它示例没有成员冲突
// object 类型是指除了原始类型以外的其它类型
const foo: object = function () {} // [] // {}
// 如果需要明确限制对象类型,则应该使用这种类型对象字面量的语法,或者是「接口」
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' } // 两边结构必须一致
七、TypeScript数组类型
// 数组类型
export {} // 确保跟其它示例没有成员冲突
// 数组类型的两种表示方式
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
// 案例 -----------------------
// 如果是 JS,需要判断是不是每个成员都是数字
// 使用 TS,类型有保障,不用添加类型判断
function sum (...args: number[]) {
return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3) // => 6
八、TypeScript 元组类型
一种特殊的数据结构,元组就是一个明确元素数量以及每个元素类型的一个数组。
// 元组(Tuple)
可以用来在函数中返回多个返回值
export {} // 确保跟其它示例没有成员冲突
const tuple: [number, string] = [18, 'zce'] // 两边结构不一致就会报错
// const age = tuple[0]
// const name = tuple[1]
// 数组解构获取
const [age, name] = tuple
// ---------------------
const entries: [string, number][] = Object.entries({
foo: 123,
bar: 456
})
const [key, value] = entries[0]
// key => foo, value => 123
九、枚举类型
1、可以给一组数值分别起上更好理解的名字
2、一个枚举中只会存在几个固定的值
// 枚举(Enum)
export {} // 确保跟其它示例没有成员冲突
// 用对象模拟枚举
// const PostStatus = {
// Draft: 0,
// Unpublished: 1,
// Published: 2
// }
// 标准的数字枚举
// enum PostStatus {
// Draft = 0, // 默认也是0
// Unpublished = 1,
// Published = 2
// }
// 数字枚举,枚举值自动基于前一个值自增
// enum PostStatus {
// Draft = 6,
// Unpublished, // => 7
// Published // => 8
// }
// 字符串枚举
// enum PostStatus {
// Draft = 'aaa',
// Unpublished = 'bbb',
// Published = 'ccc'
// }
// 常量枚举,不会侵入编译结果
const enum PostStatus {
Draft, // 默认是0
Unpublished,
Published
}
const post = {
title: 'Hello TypeScript',
content: 'TypeScript is a typed superset of JavaScript.',
status: PostStatus.Draft // 3 // 1 // 0
}
// PostStatus[0] // => Draft
十、函数类型
// 函数类型
export {} // 确保跟其它示例没有成员冲突
// 函数声明
// 形参和实参必须完全一致,可选参数和默认参数必须要出现在参数列表的最后
// 任意个数可以使用es6的reset操作符
function func1 (a: number, b: number = 10, ...rest: number[]): string {
return 'func1'
}
func1(100, 200)
func1(100)
func1(100, 200, 300)
// -----------------------------------------
// 函数表达式
const func2: (a: number, b: number) => string = function (a: number, b: number): string {
return 'func2'
}
十一、任意类型
// 任意类型(弱类型)
export {} // 确保跟其它示例没有成员冲突
function stringify (value: any) {
return JSON.stringify(value)
}
stringify('string')
stringify(100)
stringify(true)
let foo: any = 'string'
foo = 100
foo.bar()
// any 类型是不安全的 不建议使用
十二、隐式类型推断
// 隐式类型推断
export {} // 确保跟其它示例没有成员冲突
let age = 18 // number
// age = 'string' // 类型错误
let foo // Any
foo = 100
foo = 'string'
// 建议为每个变量添加明确的类型标注
十三、类型断言
// 类型断言 辅助代码中每个成员的类型。编译中的概念,编译过后就不会存在了
export {} // 确保跟其它示例没有成员冲突
// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0)
// const square = res * res
// 第一种 as 关键词
const num1 = res as number
// 第二种 <>
const num2 = <number>res // JSX 下不能使用 与标签冲突
十四、接口
一种规范或者契约,用来约定对象的结构,使用一个接口必须要遵循这个接口的全部约定,TS中接口最直观的体现就是用来去约定一个对象中具体应该有哪些成员,而且这些成员的类型又是什么样子的
接口就是用来约束对象的结构,一个对象去实现一个接口,它就必须要拥有这个接口当所约束的所有的成员。只是用来为有结构的数据做类型约束,实际中并无实际意义
// 接口
export {} // 确保跟其它示例没有成员冲突
// 定义一个接口
interface Post {
title: string
content: string
}
// 参数类型就设置成Post接口
function printPost (post: Post) {
console.log(post.title)
console.log(post.content)
}
printPost({
title: 'Hello TypeScript',
content: 'A javascript superset'
})
十五、接口补充
// 可选成员、只读成员、动态成员
export {} // 确保跟其它示例没有成员冲突
// -------------------------------------------
interface Post {
title: string
content: string
subtitle?: string // 可有可无即string|undefined 可选成员
readonly summary: string // 只读成员
}
const hello: Post = {
title: 'Hello TypeScript',
content: 'A javascript superset',
summary: 'A javascript'
}
// hello.summary = 'other'
// ---------------------------------动态成员
interface Cache {
[prop: string]: string
}
const cache: Cache = {}
cache.foo = 'value1'
cache.bar = 'value2'
十六、类
作用:描述一类具体事物的抽象特征。用来描述一类具体对象的抽象成员
ES6以前,函数+原型 模拟实现类
ES6开始,JavaScript中有了专门的class
TS增强了class的相关语法,如对类的成员有访问修饰符,还有抽象类的概念
// 类(Class)
export {} // 确保跟其它示例没有成员冲突
// 类的属性在使用之前必须要现在类型当中去声明,目的就是为给属性做类型标注
class Person {
// ts中类的属性必须要有初始值,可以在等号后面去赋值,或者在构造函数中去初始化
name: string // = 'init name'
age: number
constructor (name: string, age: number) {
this.name = name
this.age = age
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
}
}
十七、类的访问修饰符
// 类的访问修饰符
export {} // 确保跟其它示例没有成员冲突
class Person {
public name: string // = 'init name' 公有成员
private age: number // 私有属性只能在类的内部使用
protected gender: boolean // 受保护的 只能在内部使用 只允许在子类中去访问的成员
constructor (name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
console.log(this.age)
}
}
class Student extends Person {
private constructor (name: string, age: number) {
super(name, age)
console.log(this.gender)
}
static create (name: string, age: number) {
return new Student(name, age)
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
// console.log(tom.age)
// console.log(tom.gender)
const jack = Student.create('jack', 18)
十八、类的只读属性
// 类的只读属性
export {} // 确保跟其它示例没有成员冲突
class Person {
public name: string // = 'init name'
private age: number
// 只读成员 readonly跟在访问修饰符的后面
protected readonly gender: boolean
constructor (name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
console.log(this.age)
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
// tom.gender = false
十九、类与接口
// 类与接口
export {} // 确保跟其它示例没有成员冲突
interface Eat {
eat (food: string): void
}
interface Run {
run (distance: number): void
}
// 实现这两个接口 必须要有对应的成员
class Person implements Eat, Run {
eat (food: string): void {
console.log(`优雅的进餐: ${food}`)
}
run (distance: number) {
console.log(`直立行走: ${distance}`)
}
}
class Animal implements Eat, Run {
eat (food: string): void {
console.log(`呼噜呼噜的吃: ${food}`)
}
run (distance: number) {
console.log(`爬行: ${distance}`)
}
}
二十、抽象类
// 抽线类 abstract
export {} // 确保跟其它示例没有成员冲突
// 定义抽象类后只能被继承
abstract class Animal {
eat (food: string): void {
console.log(`呼噜呼噜的吃: ${food}`)
}
// 抽象方法。不需要方法体,父类有抽象方法,子类就必须要去实现这样一个方法
abstract run (distance: number): void
}
class Dog extends Animal {
run(distance: number): void {
console.log('四脚爬行', distance)
}
}
const d = new Dog()
d.eat('嗯西马')
d.run(100)
二十一、泛型
在定义函数接口或者类的时候没有指定具体的类型,使用的时候才去指定具体类型,极大程度复用代码
// 泛型
定义时不能明确的类型定义成一个参数,使用时再去传递这样的一个类型参数
export {} // 确保跟其它示例没有成员冲突
function createNumberArray (length: number, value: number): number[] {
const arr = Array<number>(length).fill(value)
return arr
}
function createStringArray (length: number, value: string): string[] {
const arr = Array<string>(length).fill(value)
return arr
}
// <T> 泛型参数,不明确的类型都用T代表
function createArray<T> (length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
// const res = createNumberArray(3, 100)
// res => [100, 100, 100]
const res = createArray<string>(3, 'foo')
二十二、类型声明
兼容一些npm 模块。
ts中引入第三方模块,如果该模块不包含所对应的类型声明文件,就尝试去安装所对应的类型声明模块,类型声明模块一般就是@types/模块名。如果没有就只能用declare语句声明所对应的模块类型
// 类型声明
import { camelCase } from 'lodash'
import qs from 'query-string' // 该模块内部已经集成类型声明文件
qs.parse('?key=value&key2=value2')
// 声明
// declare function camelCase (input: string): string
const res = camelCase('hello typed')
export {} // 确保跟其它示例没有成员冲突
yarn add @types/lodash 安装之后就可以注释调上述declare