TypeScript语言规范与基本应用

在前端快速发展的今天,如果不能时刻保持学习就会很快被淘汰。分享一下对TypeScript相关知识的学习,文章有点长,希望对大家有所帮助。每天进步一点点。

一、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可以说是前端领域中的第二语言,特别适用于长周期开发的大项目。

二、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');

三、TypeScript 配置文件

tsc命令不仅仅可以编译指定的文件,还可以编译整个项目【整个工程】

// 1、初始化配置文件  yarn tsc --init
// 2、打开tsconfig.json文件,修改相应的配置
// 一些常用配置介绍:
// target:设置编译后的JavaScript所采用的的ECMAScript标准
// moudle:输出代码采用模块化的方式
// sourceMap:是否开始源代码映射
// outDir:编译结果输出到的文件夹
// rootDir:源代码【.ts文件】所在的文件夹
// strict: 是否开始严格模式
// strictNullChecks:检查变量是否为空

// 3、设置过后,如果使用tsc命令运行指定文件,配置是不会生效的

四、TypeScript 数据类型

1、原始类型

// 在非严格模式下,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

2、Object类型

// Object类型 可以是数组对象函数等非原始类型
const obj1: object = function() {} // [] // {}
// 如果要定义对象类型,可以使用对象字面量的方式去定义【更专业的方式是用接口】
const obj2: { foo: number, bar: string } = { foo: 123, bar: 'abc'}

3、数组类型

// 使用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

4、元组类型

// 明确元素数量以及每个元素类型的数组
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'})

5、枚举类型

// 特点: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关键字

6、函数类型

// 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'
}

7、任意类型

// typescript 不会对any这种类型做类型检查,下面的语句在语法上都不会报错,存在安全问题
function stringify(value: any) {
	return JSON.stringify(value)
}
stringify('string')
stringify(123)
stringify(true)

let temp: any = 'abc'
temp = 123
temp = true

8、隐式类型推断

// typescript 在变量定义的时候会进行类型推断
let age = 18	// 推断为number类型
age = 'abc'		// 报错
let name		// 推断为any类型
// 建议在声明定义的时候给出类型注解,便于后期阅读理解代码

9、类型断言

// 明确告诉typescript,我们的数据类型是什么
// 第一种方式 as
const num1 = res as number	// 建议使用第一种
// 第二种方式 <>
const num2 = res 	// JSX下会产生冲突,不能使用

五、TypeScrip 接口

// 接口就是用来约束对象的结构,一个对象去实现一个接口,那它就必须去拥有这个接口中所约束的所有成员
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'

六、typescript 类的使用

类是面向对象编程中最重要的概念【类的作用:描述一类具体事务的抽象特征】

ES6之前,通过函数 + 原型 模拟实现类;ES6开始,JavaScript中有了专门的class,而且TypeScript增强了class的相关语法

1、基本使用

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!')

2、访问修饰符

// 额外增加的三个访问修饰符: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,外部将不能被实例化,但是可以被继承

3、只读属性

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.

4、类与接口

类与类之间会有一些公共的特征,我们可以用接口去抽象类

// 一个接口只去约束一个能力,让一个类型同时去实现多个接口
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('爬行')
	}
}

5、抽象类

// 抽象类与接口类似,也是约束子类中必要要有某一个成员;不同于接口的是,抽象类可以包含一些具体的实现
// 定义抽象类,在 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)

6、泛型

是指在定义函数接口或者类的时候,不指定具体类型,而是在使用的时候再去指定具体类型的特征,可以尽可能的复用代码。

把定义时不能明确的类型变成一个参数,使用的时候再去传递一个这样的类型参数

// 数组的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' ]

7、类型声明

一个成员在定义的时候没有声明一个明确的类型,在使用的时候需要再单独为它做出一个声明

// 在 TypeScript中引用第三方模块,如果模块当中不包含所对应的类型声明文件
// 需要尝试去安装,一般类型声明文件是 @types/对应的模块名
// 如果也没有对应的类型声明模块,就只能使用 declare 语句声明对应的模块类型

import { camelCase } from 'lodash'
declare function camelCase (input: string): string

const res = camelCase('helle typed')

你可能感兴趣的:(前端进阶,typescript,前端,大前端)