文章内容输出来源:拉勾大前端高薪训练营
TypeScript 概述
TypeScript 是基于 JavaScript 基础之上的编程语言,是 JavaScript 的超集(superset) 或者叫 扩展集 。任何一种 JavaScript 运行环境都支持
TypeScript 在JavaScript之上多了一些扩展特性:类型系统、ES6+,最终编译成原始的JavaScript。
TypeScript 解决 JavaScript 自由的类型系统的问题,大大提高代码的可靠程度。
相比较于 Flow ,TypeScript 功能更为强大,生态也更健全、更完善。
目前 Angular/Vue.js 3.0 已经使用 TypeScript。
TypeScript 是前端领域中的第二语言
缺点:(相对于JavaScript)
- 语言本身多了很多概念,但是 TypeScript 属于[渐进式]:即便什么特性都不知道,可以立马按照JavaScript 标准语法来编写代码,可以了解一些特性,使用一些特性
- 项目初期,TypeScript 会增加一些成本
基本使用
tsc 命令 作用:编译TypeScript文件,编译过程中先去检查代码中的类性使用异常,移除掉一些类型注解之类的扩展语法,自动转换ES的新特性。
//初始化 package.json 用来管理项目的依赖项
yarn init -yes
//作为项目的开发依赖安装,它提供 tsc 命令
yarn add typescript --dev
//编译文件
yarn tsc index.ts
tsc 命令 不仅可以编译指定的某个ts文件,还可以编译整个工程。
一般编译项目之前,先创建一个 typescript 的配置文件
//生成 tsconfig.json 文件
yarn tsc --init
使用,直接运行命令:yarn tsc
TypeScript 基本应用
1、原始数据类型
标准库就是内置对象所对应的声明
Symbol、Promise 都是 ES2015标准库内置的
console 是DOM 标准库内置的
严格模式下 string、number、boolean 不能为空null
非严格模式下 可以为空
const a: string = 'footbar'
const b: number = 100 //NaN Infinity
const c: boolean = true // false
// const d: string = null
const e: void = undefined
const f: null = null
const g: undefined = undefined
// const h: symbol = Symbol()
2、中文错误信息
yarn tsc --locale zh-CN
VScode 错误提示可以改配置: typescript locale : zh-CN
不推荐这么做,因为不利于搜索引擎去搜索错误信息
3、作用域
确保跟其它示例没有成员冲突 的办法:
//第一种
(function(){
const a = 123
})()
//第二种 每个文件添加
export {}
4、Object 类型
- object 不单指对象,指除了原始类型以外的其他类型
- 对象的类型限制可以使用类似字面量语法的方式,更专业的方式是接口
const foo: object = function () {}
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
5、数组类型
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
function sum(...args: number[]){
return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3)
6、元组类型:
明确元素的个数和每个元素的类型
react 中useState 这样的 Hooks 函数返回的就是元组类型
ES2017中提供 Object.entries()方法获取对象中所有的键值数组,得到的每一个键值就是元组,因为它是固定长度的
const tuple: [number, string] = [18, 'zs'];
// const age = tuple[0]
// const name = tuple[1]
const [age, name] = tuple
Object.entries({
foo: 123,
bar: 456
})
7、枚举类型:
- 默认枚举类型的值从0开始累加,也可以设置第一个值,后面的值在这个值基础之上累加,枚举类型的值也可以为字符串,必须每个成员设置值。
- 枚举类型 会入侵我们到我们运行时的代码(影响我们编译后的结果),编译成双向键值对的对象
- 如果确定代码中不会使用索引器的方式使用枚举(PostStatus[0]),可以使用常量枚举,enum 前面添加 const,这样在编译过程中枚举会移除掉,用到枚举值的地方替换成对应的枚举值。
// const PostStatus = {
// Draft: 0,
// Unpublished: 1,
// Published: 2
// }
// enum PostStatus {
// Draft = 0,
// Unpublished = 1,
// Published = 2
// }
enum PostStatus {
Draft,
Unpublished,
Published
}
// enum PostStatus {
// Draft = 6,
// Unpublished,
// Published
// }
// enum PostStatus {
// Draft = 'aaa',
// Unpublished = 'bbb',
// Published = 'ccc'
// }
const post = {
title: 'hello typescript',
content: 'typescript is a typed superset of javascript',
status: PostStatus.Draft, //1, 0
}
编译之后
var PostStatus;
(function (PostStatus) {
PostStatus[PostStatus["Draft"] = 0] = "Draft";
PostStatus[PostStatus["Unpublished"] = 1] = "Unpublished";
PostStatus[PostStatus["Published"] = 2] = "Published";
})(PostStatus || (PostStatus = {}));
var post = {
title: 'hello typescript',
content: 'typescript is a typed superset of javascript',
status: PostStatus.Draft,
};
常量枚举
const enum PostStatus {
Draft,
Unpublished,
Published
}
const post = {
title: 'hello typescript',
content: 'typescript is a typed superset of javascript',
status: PostStatus.Draft, //1, 0
}
// 编译后
var post = {
title: 'hello typescript',
content: 'typescript is a typed superset of javascript',
status: 0 /* Draft */,
};
8、函数类型:
函数可以采用 函数申明和函数表达式两种方式生成。
函数申明类型约束:
- 函数每个参数后面添加类型注解,返回值的类型添加在 括号 后面。
- 形参和实参 类型和个数必须要相同
- 可选参数,在参数名称后面添加 '?',也可以使用es6的参数默认值的方式,这两种都必须放在参数列表的最后
- 任意个数的参数使用 rest关键词(...rest)
function func1( a: number, b?:number, c: number = 10, ...rest: number[] ): string {
return 'func1'
}
func1(100, 200);
func1(100);
函数表达式类型限制:
- 可以使用相同的方式限制函数的参数和返回值的类型
- typescript能根据函数表达式推断出这个变量的类型
- 如果回调函数的方式,必须约束回调函数形参的类型,可以使用类似箭头函数的方式去表示参数可以接收什么的函数
const func2 = function(a: number, b: number): string {
return 'func2';
}
const func3: (a: number, b:number) => string = function(a: number, b: number): string {
return 'func2';
}
9、任意类型:
存在安全问题,不对any做类型检查,任何类型语法上都不会报错。
any 是不安全的,轻易不要使用。
function stringify( value: any ){
return JSON.stringify(value)
}
stringify('string')
stringify(123)
stringify(true)
10、隐式类型推断:
如果没有明确表明类型,typescript会根据变量的使用情况推断变量的类型。
如果无法推断就标注为any
建议给每个变量添加明确的类型,因为这样会便于后期更直观的理解我们的代码
let age = 18;
age = 'string'; //报错
let foo
foo = 100
foo = 'string'
11、类型断言:
两种方式:as 、<>
断言是编译过程中的概念,转换是运行阶段的概念
类型断言不是类型转换,代码编译过后断言就不存在了
const nums = [110, 120, 119, 112];
const res = nums.find(i => i > 0);
// const square = res * res;
// 第一种 断言方式
const num1 = res as number;
// 第二种 断言方式
const num2 = <number>res; // 会jsx 标签产生冲突
12、接口(interfaces)
可以理解成 一种规范或者契约,用来约束对象的结构,使用一个接口,就必须遵守接口全部的约束
- interface 开头,多个字段用 ,/; 也可以不加
- 可选成员、只读成员、动态成员
interface Post {
title: string,
content: string;
subtitle?: string //可选成员
readonly summary: string //只读成员
}
function printPost(post: Post){
console.log(post.title);
console.log(post.content);
}
const hello: Post = {
title: 'Hello TypeScript',
content: 'A javascript superset',
summary: 'A javascript'
}
printPost(hello);
// hello.summary = 'jajja'; //报错
//动态成员
interface Cache{
[key: string]: string
}
const cache: Cache = {}
cache.foo = 'values';
cache.bar = 'hahha';
13、类 Classes
作用:用来描述一类具体事物的抽象特征
代码中:用来描述一类具体对象的抽象成员
ES6 中开始有专门的class,TypeScript 增强了 class 的相关语法
- 需要先明确申明类有的一些属性
- 属性必须设置默认值(可以在类中定义通过‘=’添加,也可以在构造函数中初始化)
class Person{
name: string
age: number
constructor(name: string, age: number){
this.name = 'init name';
this.age = 18
}
sayHi( msg: string): void{
console.log(`I am ${this.name}, ${msg}`)
}
}
13.1、类的访问修饰符
控制类中成员的可访问级别
- 默认是 public,
- private私有属性:不能通过外部访问,只能类的内部使用
- protected受保护的:不能通过外部访问,只允许在子类中访问
- readonly只读属性
class Person{
public name: string
private age: number
protected readonly gender: boolean
constructor(name: string, age: number){
this.name = 'init name';
this.age = 18
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)
// console.log(tom.age)// 报错
// console.log(tom.gender)// 报错
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 jack = new Student('jack', 18); //报错
const jack = Student.create('jack', 18);
13.2、类 与 接口
接口:抽取公共的特征
实现接口必须要有对应的所有的成员
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}`)
}
}
13.3、抽象类
class 前面加 abstract, 只能被继承,不能使用new创建的对应的实例对象,必须使用子类继承这个类型
- 抽象方法需要使用 abstract 修饰,不需要方法体
- 父类中有抽象方法时,子类必须实现它
可以使用vscode 的代码修正功能,自动生成对应的方法实现
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);
13.4、泛型(Generics)
定义接口、函数或累的时候没有指定具体的类型,使用的时候再去指定具体的类型
目的:极大程度的复用代码
//创建指定长度的数组
function createNumberArray(length: number, value: number): number[]{
const arr = Array<number>(length).fill(value);
return arr;
}
const res0 = createNumberArray(3, 100);
//
function createArray<T>(length: number, value: T): T[]{
const arr = Array<T>(length).fill(value);
return arr;
}
const res1 = createArray<number>(3, 100); //[100, 100, 100]
const res2 = createArray<string>(3, 'foo'); // ['foo', 'foo', 'foo']
13.5、类型申明(Type Declaration)
定义的时候没有明确的申明,使用时 申明(declare)
为了兼容普通的js模块
在TypeScript中引入第三方模块,如果模块中不包含对应的类型申明文件,我们就要安装对应的类型申明模块,"@types/模块名", 如果没有 只能自己用 declare 语句申明所对应的模块类型
import { camelCase } from 'lodash'
declare function camelCase(input: string): string
const res = camelCase('hello typed');