简介
TypeScript是JavaScript为基础的一门语言,是JavaScript的超集,实际运行时会将其编译成JavaScript。
环境搭建
- 安装Node.js,并安装
- 地址:https://nodejs.org/en/
- 使用npm全局安装typescript
- 进入cmd界面,输入:
npm i typescript -g
- 进入cmd界面,输入:
- 创建一个
.ts
类型文件 - 编译ts文件
- 打开命令行界面,进入ts文件所在目录
- 编译:
tsc xxx.ts
- 执行完这一步后会发现当前目录出现了一个同名的
.js
文件
- 执行完这一步后会发现当前目录出现了一个同名的
类型
-
类型声明
-
通过为TS中的变量设置类型,在赋值该变量时需要符合类型声明,否则就会报错。
-
使用
type
关键字自定义类型(其实就是为变量重命名) -
语法如下:
// 赋值给变量的值需要符合类型声明 let 变量: 类型; let 变量: 类型 = 值; // 函数也同样可以设置参数类型、返回值类型 function fnc(变量: 类型, 变量: 类型) : 返回值类型 { // do something } // type关键字 // 这里个人感觉就是字面量属性换了个名字 type 自定义类型 = 值 let 变量: 自定义类型 = 值 // 这里的值必须和上面自定义类型的值一致 // 对象(为对象结构重命名) type Person { name: string, age: number, gander: number } // 这里的obj结构必须和Person一致 let obj: Person = { name: 'Yuiai', age: 18, gander: 1 }
-
-
类型注解和类型推断
一些简单定义无需类型注解,因为TypeScript会自动推断出其数据类型,如下:
// 这里的one、two变量会自动推断成number(毕竟已经设置number类型的值了) let one = 1 let two = 2 // 这里的three的类型也被自动推断为number类型 // 这里会等于3 而不是报错,因为 one、two其类型已经被推断为number了 let three = one + two // 这里的例子与上文其实一致 function getTolal(num1: number, num2: number) { // 这里的返回值 也被自动推断为number类型 // 所以如果用该函数的返回值作为初始值的变量其类型也被推断为number return num1 + num2 } // 这里的total自动被推断为number类型 let total = getTotal(1, 2) // TypeScript会自动推断出对象属性的数据类型(其实跟上文的缘由一致) // name: string // age: number let Yuiai = { name: 'Fuyuumiai', age: 10 }
工作使用问题(潜规则)
- 如果
TS
能够自动分析变量类型,我们就什么也不需要做了; - 如果
TS
无法分析变量类型的话,我们就需要使用类型注解。
- 如果
-
数据类型
类型 取值 描述 number 22、-33、3.41 数字 string “hi”、‘hi’、`hi` 字符串 boolean true、false 布尔值 字面量 其本身,如 let num: 10;
相当于常量 any 任意值 任意类型,可赋任意值 void 空值(undefined) 表示函数无返回值或(undefined) never 没有值 不能是任何值 object {name: ‘yuiai’} 任意JS引用类型 array [1, 2, 3]、[‘a’, ‘b’, ‘c’] 同一指定类型数据构成的数组 tuple [1, 2] 固定长度数组(类似python的元组) enum enum Color {Red, Green, Blue} 可以由枚举的值得到它的名字 实例如下:
// 使用字面量作为数据类型 // 这里随后只能将num赋值为10(类似与常量的概念) let a: 10; a = 10; // true a = 11; // false // 联合类型 // 可以使用 `|` 分隔多个指定的数据类型,也可以用来分隔多个字面量 // 这样就可以使用多个数据类型或字面量中的一种 let b: 1 | 2; let c: number | string; function fnc(num: number): number | string{ if (num > 0) { return 'ok' } else { return 0 } } // any类型的变量在之后可以赋值任何类型的值,相当于普通js的变量 // 未设置类型的变量的默认类型就是any // 但是这样typescript就失去了意义,所以不推荐使用any类型 let c: any; // 未知类型(unknown) // 有时候变量的类型我们不得而知,所以就可以设置成`unknown` let d: unknown; // void(表示函数的返回值为空) // 由于JavaScript函数默认返回undefined,所有return undefined、null是不会报错的 function sum(): void{} // never(表示不能有返回值,要区分void) // 用来抛出异常,了解即可 function fn(): never { throw new Error('错误信息') } // object (对象类型/引用类型) let e: object; e = {}; //true e = function(){}; //true e = new String('hi'); // true e = 'hi'; // false // array(数组类型) // 两种设置方式: // 类型[]、Array<类型> let arr0: string[]; let arr1: Array<number>; arr0 = ['a', 'b', 'c'] arr1 = [1, 2, 3] // tuple(元组类型) // 用于表示一个已知数量、数据类型的数组,各个元素的数据类型可以不同,如下: let tup: [string, number]; tup = ['a', 1]; // enum(枚举类型) // 默认情况下从0开始为元素编号 enum Color {Red, Green, Blue} Color.Red // 0 // 也可以手动为元素设置值 // 数字值会自动按序排下去,其他类型的值需要为元素全部设置值 enum Gender {FaMale = 4, Male} Gender.Male // 5
any
类型与unknown
类型的异同:-
相同之处:
- 可以赋予任意类型的值;
unknown
其实就是一个类型安全的any
。
-
不同之处:
any
类型的变量可以被赋值给任意类型的变量,即使any
类型的变量最后赋的值的类型与当前被赋值变量类型不同;unknown
类型的变量不能被直接赋值给其他变量(需进行类型判断或类型断言)
-
实例:
let s = string; // any类型 let a: any; a = 1; s = a; // true // unknown类型 let b: unknown; b = 'hi'; s = b; // false // 如果unknown进行了类型判断、类型断言的话就可以赋值 // 类型判断 if (typeof b === 'string') { s = b; // true } // 类型断言(相当于断定变量的类型),语法有如下两种 // 变量 as 类型 // <类型>变量 s = b as string; // true s = <string> b; // true
-
对象结构定义
-
创建对象结构
-
语法
{属性名:类型,属性名:类型}
-
实例:
let a = { name: string, age: number } a = {name: 'yuiai', age: 18} //true a = {name: 'yuiai'} // false,缺少age属性 // 使用`?`表示对象属性可选 let b = { name: string, age?: number } b = {name: 'yuiai', age: 18} // true b = {name: 'yuiai'} // true,因为age属性是可选的 // 使用 `[属性名:string]:类型`,表示对象不固定属性值,类似ES6的rest参数 // 如下实例,这样就表示name属性为必填,其他属性个数不定 let c = { name: string, // 这样表示属性名的类型为string,属性值的类型为任意类型 [propName: string]: any } c = {name: 'yuiai'} //true c = {name: 'yuiai', age: 18, sex: '男'} // true
-
函数结构定义
-
创建函数结构(非函数)
-
语法
(形参:类型,形参:类型...) => 返回值类型
-
语法:
// 使用箭头函数的方式定义 let fnc: (num: number) => string; // true fnc = (1) => { return 'hi' } // true fnc = function(1){ return 'hi' }
-
定义函数
// 使用Function、function进行定义 // 定义一个参数为number类型,返回值为string类型的函数 // Function let fnc: Function; fnc = function(num: number): string{ return 'hi' } // function function fnc0(num: number): string{ return 'hi' } // 还可以使用联合类型定义复数的返回值类型 let fnc1 = function(num: number): string | number{ if (num > 0) { return num } return 'hi' } // 当然也可以用到上文的箭头函数形式进行定义 let fnc: () => string = () => 'hi'
编译选项
在编写typescript
时,如果每次变更都要使用tsc
命令进行重新编译,很麻烦。所以我们需要一种会在我们变更ts文件后自动编译的方法。
-
方法:
- 使用
tsc xxx.ts -w
的方式监听某一个ts
文件- 缺点:只能对一个文件进行监听,如果需要对其他
ts
文件进行监听,则需要重复上述命令行操作。
- 缺点:只能对一个文件进行监听,如果需要对其他
- 使用配置文件
tsconfig.json
,以下为vscode
使用tsconfig.json
的操作方法- 使用
vscode
打开我们要操作的文件夹,打开终端,输入tsc -init
命令 - 此时会在文件夹根目录出现一个
tsconfig.json
文件- 如果编译后的js输出目录不保存在根目录,可以打开
tsconfig.json
找到outDir
属性更改成要输出的目录;
- 如果编译后的js输出目录不保存在根目录,可以打开
- 在vscode中, mac: Command+ ⬆️ + B ; windows: Ctrl + Shift + B,设置监听,当该文件夹内的任一
ts
文件变更之后都会进行重新编译。
- 使用
- 使用
-
tsconfig.json
配置选项tsconfig.json
是ts
编译器的配置文件,ts
编译器可以根据它的信息来对代码进行编译。-
include
:用来指定哪些路径下的ts
文件需要编译-
示例:仅编译
src
、tests
目录下的ts
文件(需注意,路径是相对于tsconfig.json
){ /* ** 表示任意目录 * 表示任意文件 */ "include": ["src/**/*", "tests/**/*"] }
-
-
exclued
:用来指定不需要编译的文件目录(实际不常用,且具有默认值)-
默认值:
["node_modules", "bower_components", "jspm_packages"]
-
示例:排除
src
目录下的no-compile
目录{ "exclued": ["src/no-compile/**/*"] }
-
-
extends
:定义被继承的配置文件(类似于引入外部配置文件)-
示例:引入
configs
目录下的base.json
{ "extends": "./configs/base.json" }
-
-
files
:指定要进行编译的文件列表,很麻烦,实际不常用-
示例:
{ "files": [ "app.ts", "index.ts" ] }
-
-
compilerOptions
:编译器的选项配置-
target
:用来指定ts
被编译为的ES
版本-
候选值:所有
ES
版本,目前有:‘es3’, ‘es5’, ‘es6’, ‘es2015’, ‘es2016’, ‘es2017’, ‘es2018’, ‘es2019’, ‘es2020’, ‘esnext’
-
示例:
ts
代码将会编译成ES5
版本{ "compilerOptions": { "target": "ES5" } }
-
-
module
:指定要使用的模块化的规范-
候选值:none和所有模块化规范,目前有:
‘none’, ‘commonjs’, ‘amd’, ‘system’, ‘umd’, ‘es6’, ‘es2015’, ‘es2020’, ‘esnext’
-
示例:
ts
模块化语句(引入、导出)将编译成ES6
的模块化语句{ "compilerOptions": { "module": "ES6" } }
-
-
lib
:指定代码运行时要包含的库(宿主环境),默认包含运行所需库。可以设置错误值,然后在控制台查看能设置的默认值。
-
outDir
:用来指定编译后文件所在的目录-
示例:将编译后的文件保存在
dist
文件夹内{ "compilerOptions": { "outDir": "dist" } }
-
-
outFile
:用来指定将编译后的全局作用域的代码汇总到一起的文件-
注意:如果需编译的文件中存在模块化语句,且
module
不是amd
、system
编译会报错。 -
示例:将编译后的文件汇总保存在
dist
文件夹下的app.js
中{ "compilerOptions": { "outFile": "dist/app.js" } }
-
-
allowJs
:是否对js文件进行编译,默认为false -
checkJs
:是否检查js代码是否符合ts
语法规范,默认为false -
removeComments
:是否移除注释,默认为false -
noEmit
:是否不生成编译后的文件,默认为false,既生成编译后的文件 -
noEmitOnError
:是否在被编译文件出错后继续生成编译文件,默认为false,既生成。 -
alwaysStrict
:编译后的文件是否采用严格模式,默认为false。 -
noImplicitAny
:是否不允许隐式的any
类型(如:未声明类型、设置初始值的函数参数),默认为false,既允许。 -
noImplicitThis
:是否不允许不明确的this
,默认是false,既允许。 -
strictNullChecks
:是否严格检查空值,默认为false -
strict
:严格模式的总开关,如果设置为true,那么上述严格检查将会启用,如果关闭需单独设置。
-
-
面向对象
这段介绍ts
中面向对象的语法。面向对象就是将现实事物抽象化,将其属性抽离,将其行为封装成方法。
-
类(class)
-
属性(可以通过设置
readonly
为属性设置仅读)-
实例属性:不加
static
标识的类属性,只能通过实例化对象获取到 -
静态属性:加
static
标识的类属性,通过类可以直接获取到 -
示例:
class Person { // 实例属性 name: string; // 静态属性 static stateMent: string = '这是一个测试类'; // 可读实例属性、静态属性 readonly age: number = 18; static readonly text: string = '测试可读属性。'; // 构造函数 constructor(name: string){ this.name = name } } // 通过实例化对象获取实例属性 let per: Person = new Person('yuiai'); per.name // yuiai // 通过类获取静态属性 Person.stateMent // 这是一个测试类 // 无法更改仅读属性的值 per.age = 20; // false Person.text = '更改可读属性'; // false
-
-
方法(方法不能进行重载,但是如果子类继承父类的方法可以进行重写)
-
实例方法:与实例属性类似,不加
static
关键字,需通过实例化对象进行调用 -
静态方法:与静态属性类似,需加
static
关键字,通过类进行调用。 -
示例:
class Person { name: string; constructor(name: string){ this.name = name } // 实例方法 sayName(): string{ return `我的名字是${this.name}` } // 静态方法 static sayState(): void{ console.log('这是一个静态方法!') } } class Test extends Person { // 继承后方法的重写 sayName():string { return `My name is Yuiai` } } let per: Person = new Person('yuiai') console.log(per.sayName()) // 我的名字是yuiai Person.sayState() // 这是一个静态方法!
-
-
继承(
ts
的继承的与ES6
的继承无太大区别,个人感觉就是添加了类型)-
super
关键字:父类的指针,可以通过其调用父类的方法 -
示例:
class Animal { name: string constructor(name: string){ this.name = name } sayName(): void{ console.log(`我的名字是${this.name}`) } } class Dog extends Animal { age: number // 需要注意的是,如果要重写构造函数,需要使用super()调用父类的构造函数 constructor(name: string, age: number){ // 使用super()调用父类构造函数 super(name) this.age = age } suggest(): void { // 使用super.xxx调用父类的方法 super.sayName() console.log(`我的年龄是${this.age}`) } }
-
-
抽象类(
abstract
)抽象类是将一类具有相同特征的对象的属性与方法抽象、汇总到一起,并专门给这些对象继承的类(生而为爷)叫做抽象类,要点如下:
-
不能被实例化;
-
类中的抽象方法一定要实现;
-
示例:
// 抽象类 abstract class Animal{ race: string constructor(race: string){ this.race = race } // 抽象方法 abstract sayRace(): void } // 继承 class Cat extends Animal{ // 这里必须要实现这个抽象方法 sayRace() { console.log(`我的种族是${this.race}`) } } new Animal('喵喵') // false new Cat('喵喵').sayRace() // 我的种族是喵喵
-
-
接口(与
java
的接口概念类似,其中全是抽象方法或属性声明)-
类似于类型声明(但是与
type
类型声明不同的是,可以重复定义,最终会进行合并) -
可以被类实现
-
示例:
// 类型声明,可以重复声明 interface Person { name: string; age: number } interface Person { gander: number } // 这里必须实现全部属性 let yuiai:Person = { name: 'Yuiai', age: 18, gander: 1 } // 类的接口 interface Fnc { name: string; age: number; say: ()=> string } // 类必须全部初始化属性、实现方法 class Yuiai implements Fnc { name: string age: number constructor(name: string, age: number){ this.name = name this.age = age } say(){ return 'hi' } }
-
-
总结
这篇文章是我边看视频,边总结编写的,基本囊括了TypeScrip的基础知识,希望能帮到大家,如果该篇文章中有错误或不足之处,忘指出,谢谢~~