在前端快速发展的今天,如果不能时刻保持学习就会很快被淘汰。分享一下对TypeScript相关知识的学习,文章有点长,希望对大家有所帮助。每天进步一点点。
TypeScript:JavaScript + 类型系统 + ES6+
TypeScript 起源于使用JavaScript开发的大型项目 。由于JavaScript语言本身的局限性,难以胜任和维护大型项目开发。因此微软开发了TypeScript ,使得其能够胜任开发大型项目。
TypeScript是JavaScript的一个超集,在JavaScript的基础上增加了类型系统和ES6新特性,TypeScrip中的类型系统的作用和Flow是类似的(避免编码过程当中的类型异常,提高开发效率以及代码的可靠程度),TypeScrip支持ES6+提供的新特性,它会自动转换这些新特性(与babel转换是类似的,TypeScrip最低可以转换为ES3版本的代码),TypeScrip支持任何一种JavaScript运行环境,
TypeScript属于渐进式的,即使一点不了解TypeScript,也可以按照JavaScript的方法去使用它。
Angular项目以及vue3.0也开始使用TypeScrip来取代之前所使用的Flow,TypeScrip可以说是前端领域中的第二语言,特别适用于长周期开发的大项目。
// 准备工作:
// 1、yarn init -yes 初始化项目
// 2、yarn add typescript --dev 安装依赖
// 3、新建ts文件,编写代码
// 可以完全按照 JavaScript 标准语法去编写代码
const fn = (name: string) => {
console.log(`Hello, ${name}`)
}
fn('typescript')
// 使用命令 yarn tsc ceshi.ts 运行
// 自动转换为ES3版本的代码,类型注解也被去掉了
// var fn = function (name) {
// console.log("Hello, " + name);
// };
// fn('typescript');
tsc命令不仅仅可以编译指定的文件,还可以编译整个项目【整个工程】
// 1、初始化配置文件 yarn tsc --init
// 2、打开tsconfig.json文件,修改相应的配置
// 一些常用配置介绍:
// target:设置编译后的JavaScript所采用的的ECMAScript标准
// moudle:输出代码采用模块化的方式
// sourceMap:是否开始源代码映射
// outDir:编译结果输出到的文件夹
// rootDir:源代码【.ts文件】所在的文件夹
// strict: 是否开始严格模式
// strictNullChecks:检查变量是否为空
// 3、设置过后,如果使用tsc命令运行指定文件,配置是不会生效的
// 在非严格模式下,string number boolean 类型默认是可以为 null 或者 undefined
const a: string = 'abc'
const b: number = Infinity // NaN // 100
const c: boolean = false // true
// 非严格模式下 void 类型值可以是null 或者 undefined,严格模式只能为undefined
const e: void = undefined // null
const d: null = null
const d: undefined = undefined
// ES6中新增的类型,如果配置文件中设置target为ES6以下就会报错
// 解决方法,1、target修改为ES6;
// 解决方法, 2、在配置文件的lib中添加ES2015,会覆盖默认标准库,还需要添加DOM(包含BOM和DOM)
// PS: 标准库,内置对象所对应的声明
const f: symbol = Symbol()
// Tips: 显示中文错误消息,yarn tsc --local zh-CN
// Object类型 可以是数组对象函数等非原始类型
const obj1: object = function() {} // [] // {}
// 如果要定义对象类型,可以使用对象字面量的方式去定义【更专业的方式是用接口】
const obj2: { foo: number, bar: string } = { foo: 123, bar: 'abc'}
// 使用Array泛型
const arr1: Array = [1, 2, 3]
// 使用元素类型加[]
const arr2: number[] = [1, 2, 3]
// 使用案例
function sum (...arg: number[]) {
return arg.reduce((pre, cur) => pre + cur, 0)
}
console.log(sum(1, 2, 3)) // 6
// 明确元素数量以及每个元素类型的数组
const arr: [number, string] = [10, 'abc']
// 可以使用数组下表访问
const num = arr[0]
const str = arr[1]
// 使用数组结构的方法提取
const [num2, str2] = arr
console.log(num, str, num2, str2)
// Object.entries()方法是以数组的方式返回对象中的键值对
// arr2也是元组类型
const arr2 = Object.entries({foo: 123, bar: 'abc'})
// 特点:1、给一组数据起一个更好理解的名字;2、一个枚举中只会存在几个固定的值
// 枚举类型的使用方式和对象是一样的
// 1、字符串枚举,需要给定每一个值[字符串枚举不常见]
// enum ResultStausStr {
// pending = 'pen',
// success = 'suc',
// error = 'err'
// }
///2、数字枚举 如果枚举中的值不指定,就会从0开始累加,如果指定了一个值,后面的值会在这个基础上进行累加
enum ResultStaus {
pending, // 0
success, // 1
error // 2
}
// enum ResultStaus {
// pending = 0,
// success = 1,
// error = 2
// }
// 枚举类型会影响编译后的结果,枚举会编译为一个双向的键值对对象,并不会移除,编译后如下:
// 双向键值对,可以通过键去获取值,也可以通过值去获取键
var ResultStaus;
(function (ResultStaus) {
ResultStaus[ResultStaus["pending"] = 0] = "pending";
ResultStaus[ResultStaus["success"] = 1] = "success";
ResultStaus[ResultStaus["error"] = 2] = "error"; // 2
})(ResultStaus || (ResultStaus = {}));
///3、如果代码中不会使用索引器访问枚举,可以使用常量枚举,就是在enum前面加上const关键字
// 1、函数声明
// 可选参数,可以使用?:方式也可以使用参数默认值的方式,都要放在最后位置
// 接受任意参数可以使用ES6的...rest
function fun (a: number, b?: string): string {
return 'fun'
}
func(100) // 报错
func(100, 200) // 报错
func(100, 'abc') // ok
func(100, 'abc', 200) // 报错
// 2、函数表达式
const fun2: (a: number, b: string) => string = function(a: number, b: string): string => {
return 'fun2'
}
// typescript 不会对any这种类型做类型检查,下面的语句在语法上都不会报错,存在安全问题
function stringify(value: any) {
return JSON.stringify(value)
}
stringify('string')
stringify(123)
stringify(true)
let temp: any = 'abc'
temp = 123
temp = true
// typescript 在变量定义的时候会进行类型推断
let age = 18 // 推断为number类型
age = 'abc' // 报错
let name // 推断为any类型
// 建议在声明定义的时候给出类型注解,便于后期阅读理解代码
// 明确告诉typescript,我们的数据类型是什么
// 第一种方式 as
const num1 = res as number // 建议使用第一种
// 第二种方式 <>
const num2 = res // JSX下会产生冲突,不能使用
// 接口就是用来约束对象的结构,一个对象去实现一个接口,那它就必须去拥有这个接口中所约束的所有成员
interface Post {
title: string
content: string
}
// 给post对象的类型设置为上面定义的Post接口
function printPost(post: Post) {
console.log(post.title)
console.log(post.content)
}
printPost({
title: 'hello',
content: 'typescript'
})
// 可选成员[subtitle]和只读成员[summary]
interface Post {
title: string
content: string
subtitle?: string
readonly summary: string
}
function printPost(post: Post) {
console.log(post.title)
console.log(post.content)
}
printPost({
title: 'hello',
content: 'typescript',
summary: 'summary'
})
// 动态成员
interface MyCache {
[key: string]: string
}
const mycache: MyCache = {}
mycache.str1 = 'value1'
mycache.str2 = 'value2'
mycache.str3 = 'value3'
类是面向对象编程中最重要的概念【类的作用:描述一类具体事务的抽象特征】
ES6之前,通过函数 + 原型 模拟实现类;ES6开始,JavaScript中有了专门的class,而且TypeScript增强了class的相关语法
class Person {
// 需要在顶部声明变量的类型
name: string // = 'init name'
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHi(msg: string): void {
console.log(`my name is ${this.name}, my age is ${this.age}, ${msg}`)
}
}
const p1 = new Person('zhagnsan', 18)
p1.sayHi('nice to meet you!')
// 额外增加的三个访问修饰符:public/private/protected
class Person {
// 公有成员[默认就是public],在类的内部,外部,子类都可以访问
public name: string // = 'init name'
// 私有成员,只能在类的内部访问
private age: number
// 受保护的成员,在自身属性和子类中都可以访问到
protected gender: boolean
constructor(name: string, age: number, gender: boolean) {
this.name = name
this.age = age
this.gender = gender
}
sayHi(msg: string): void {
console.log(`my name is ${this.name}, my age is ${this.age}, ${msg}`)
}
}
const p1 = new Person('zhagnsan', 18, true)
p1.sayHi('nice to meet you!')
console.log(p1.name) // ok
console.log(p1.age) // 报错 Property 'age' is private and only accessible within class 'Person'.
console.log(p1.gender) // 报错 Property 'gender' is protected and only accessible within class 'Person' and its subclasses.
// 构造函数的访问修饰符,默认也是public
// 1、构造函数标记为 private,外部将不能被实例化而且不能被继承,可以通过静态方法去创建
class Student extends Person {
private constructor(name: string, age: number, gender: boolean) {
super(name, age, gender)
}
static create (name: string, age: number, gender: boolean) {
return new Student(name, age, gender)
}
}
// const p2 = new Student('lisi', 20, false)
// Constructor of class 'Student' is private and only accessible within the class declaration.
const p2 = Student.create('lisi', 20, false) // OK
// 2、构造函数标记为 protected,外部将不能被实例化,但是可以被继承
class Person {
public name: string
private age: number
// readonly 只读属性,初始化后不可更改
protected readonly gender: boolean
constructor(name: string, age: number, gender: boolean) {
this.name = name
this.age = age
this.gender = gender
}
sayHi(msg: string): void {
console.log(`my name is ${this.name}, my age is ${this.age}, ${msg}`)
}
}
const p1 = new Person('zhagnsan', 19, true)
console.log(p1.name)
p1.gender = false
// 报错 Cannot assign to 'gender' because it is a read-only property.
类与类之间会有一些公共的特征,我们可以用接口去抽象类
// 一个接口只去约束一个能力,让一个类型同时去实现多个接口
interface Eat {
eat (food: string): void
}
interface Run {
run (distance: number): void
}
// 使用 implements 来实现接口约束
class Person implements Eat, Run {
eat (food: string): void {
console.log('吃饭')
}
run (distance: number): void {
console.log('站着走')
}
}
class Animal implements Eat, Run {
eat (food: string): void {
console.log('吃食')
}
run (distance: number): void {
console.log('爬行')
}
}
// 抽象类与接口类似,也是约束子类中必要要有某一个成员;不同于接口的是,抽象类可以包含一些具体的实现
// 定义抽象类,在 class 关键词前面使用 abstract
abstract class Animal {
eat (food: string): void {
console.log('吃食')
}
// 定义抽象方法 需要 abstract 去修饰
abstract run (distance: number): void
}
// 抽象类只能够继承,不能够使用 new 关键词去创建实例对象
class Dog extends Animal {
run (distance: number): void {
console.log('爬行')
}
}
const d = new Dog()
d.eat('abc')
d.run(123)
是指在定义函数接口或者类的时候,不指定具体类型,而是在使用的时候再去指定具体类型的特征,可以尽可能的复用代码。
把定义时不能明确的类型变成一个参数,使用的时候再去传递一个这样的类型参数
// 数组的fill方法是ES2016中新增的,需要在配置文件的 lib 中增加ES2016
function createStringArray(length: number, value: string): string[] {
const arr = Array(length).fill(value)
return arr
}
const r1 = createStringArray(3, 'abc')
function createNumberArray(length: number, value: number): number[] {
const arr = Array(length).fill(value)
return arr
}
const r2 = createNumberArray(3, 100)
// 使用泛型解决定义时不能明确参数类型的问题
function createArray(length: number, value: T): T[] {
const arr = Array(length).fill(value)
return arr
}
const r3 = createArray(3, 200)
const r4 = createArray(3, 'def')
console.log(r1) // [ 'abc', 'abc', 'abc' ]
console.log(r2) // [ 100, 100, 100 ]
console.log(r3) // [ 200, 200, 200 ]
console.log(r4) // [ 'def', 'def', 'def' ]
一个成员在定义的时候没有声明一个明确的类型,在使用的时候需要再单独为它做出一个声明
// 在 TypeScript中引用第三方模块,如果模块当中不包含所对应的类型声明文件
// 需要尝试去安装,一般类型声明文件是 @types/对应的模块名
// 如果也没有对应的类型声明模块,就只能使用 declare 语句声明对应的模块类型
import { camelCase } from 'lodash'
declare function camelCase (input: string): string
const res = camelCase('helle typed')