在前端快速发展的今天,如果不能时刻保持学习就会很快被淘汰。分享一下对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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 准备工作: // 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
2
3
4
5
6
7
8
9
10
11
12
13// 1、初始化配置文件 yarn tsc --init // 2、打开tsconfig.json文件,修改相应的配置 // 一些常用配置介绍: // target:设置编译后的JavaScript所采用的的ECMAScript标准 // moudle:输出代码采用模块化的方式 // sourceMap:是否开始源代码映射 // outDir:编译结果输出到的文件夹 // rootDir:源代码【.ts文件】所在的文件夹 // strict: 是否开始严格模式 // strictNullChecks:检查变量是否为空 // 3、设置过后,如果使用tsc命令运行指定文件,配置是不会生效的
四、TypeScript 数据类型
1、原始类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 在非严格模式下,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类型
1
2
3
4
5// Object类型 可以是数组对象函数等非原始类型 const obj1: object = function() {} // [] // {} // 如果要定义对象类型,可以使用对象字面量的方式去定义【更专业的方式是用接口】 const obj2: { foo: number, bar: string } = { foo: 123, bar: 'abc'}
3、数组类型
1
2
3
4
5
6
7
8
9
10
11// 使用Array泛型 const arr1: Array<number> = [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、元组类型
1
2
3
4
5
6
7
8
9
10
11
12// 明确元素数量以及每个元素类型的数组 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 特点: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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 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、任意类型
1
2
3
4
5
6
7
8
9
10
11// 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、隐式类型推断
1
2
3
4
5
6
7
8// typescript 在变量定义的时候会进行类型推断 let age = 18 // 推断为number类型 age = 'abc' // 报错 let name // 推断为any类型 // 建议在声明定义的时候给出类型注解,便于后期阅读理解代码
9、类型断言
1
2
3
4
5
6
7// 明确告诉typescript,我们的数据类型是什么 // 第一种方式 as const num1 = res as number // 建议使用第一种 // 第二种方式 <> const num2 = <number>res // JSX下会产生冲突,不能使用
五、TypeScrip 接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 接口就是用来约束对象的结构,一个对象去实现一个接口,那它就必须去拥有这个接口中所约束的所有成员 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' })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 可选成员[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、基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class 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、访问修饰符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 额外增加的三个访问修饰符: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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 构造函数的访问修饰符,默认也是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、只读属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class 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、类与接口
类与类之间会有一些公共的特征,我们可以用接口去抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 一个接口只去约束一个能力,让一个类型同时去实现多个接口 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、抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 抽象类与接口类似,也是约束子类中必要要有某一个成员;不同于接口的是,抽象类可以包含一些具体的实现 // 定义抽象类,在 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、泛型
是指在定义函数接口或者类的时候,不指定具体类型,而是在使用的时候再去指定具体类型的特征,可以尽可能的复用代码。
把定义时不能明确的类型变成一个参数,使用的时候再去传递一个这样的类型参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 数组的fill方法是ES2016中新增的,需要在配置文件的 lib 中增加ES2016 function createStringArray(length: number, value: string): string[] { const arr = Array<string>(length).fill(value) return arr } const r1 = createStringArray(3, 'abc') function createNumberArray(length: number, value: number): number[] { const arr = Array<number>(length).fill(value) return arr } const r2 = createNumberArray(3, 100) // 使用泛型解决定义时不能明确参数类型的问题 function createArray<T>(length: number, value: T): T[] { const arr = Array<T>(length).fill(value) return arr } const r3 = createArray<number>(3, 200) const r4 = createArray<string>(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、类型声明
一个成员在定义的时候没有声明一个明确的类型,在使用的时候需要再单独为它做出一个声明
1
2
3
4
5
6
7// 在 TypeScript中引用第三方模块,如果模块当中不包含所对应的类型声明文件 // 需要尝试去安装,一般类型声明文件是 @types/对应的模块名 // 如果也没有对应的类型声明模块,就只能使用 declare 语句声明对应的模块类型 import { camelCase } from 'lodash' declare function camelCase (input: string): string const res = camelCase('helle typed')
最后
以上就是诚心石头最近收集整理的关于TypeScript语言规范与基本应用的全部内容,更多相关TypeScript语言规范与基本应用内容请搜索靠谱客的其他文章。
发表评论 取消回复