相关信息
视频地址:https://www.bilibili.com/video/BV1Xy4y1v7S2?from=search&seid=335637898792380515
github:https://github.com/haveadate
码云:https://gitee.com/haveadate
email:[email protected]
初识 TS
安装
# 安装
npm i typescript -g
# 验证是否安装成功
C:\Users\haveadate>tsc -v
Version 4.1.3
初步介绍
- 以
JavaScript
为基础构建的语言; - 可以在任何支持
JavaScript
的平台中执行; - 不能被 JS 解析器直接执行(预先需要将
.ts
文件转成.js
文件); - 是一个
JavaScript
的超集; TypeScript
扩展了JavaScript
,并添加了类型;
将 .ts 文件解析成 .js 文件
tsc xxx.ts
语法
声明变量并指定类型
// 声明一个变量 stuName 同时指定它的类型为 string
let stuName: string;
// stuName 的类型设置成了 string,在以后的使用过程中,stuName 的值只能是 string 类型
stuName = '张三';
// 报错:不能将 number 类型赋值给 string 类型
stuName = 123;
$ tsc index
index.ts:6:1 - error TS2322: Type 'number' is not assignable to type 'string'.
声明变量并赋值、指定类型
// 声明一个变量并赋值,同时隐式指定变量类型是 string
let stuName = '张三';
// 抛出错误,类型不匹配
// index.ts:4:1 - error TS2322: Type 'number' is not assignable to type 'string'.
stuName = 123;
指定函数参数类型和返回值类型
// 若类型不匹配,同样会报错
function sum(num1: number, num2: number): number {
return num1 + num2;
}
TS 变量类型
基本类型
string、number、boolean
;
字面量类型
let num: 10;
,此时该变量值只能是 10,若赋其它值给 num,编译成 JS 时会报错;
联合类型
let gender = 'male' | 'female';
,此时 gender 可以为 'male'
,也可以为 'female'
;不仅仅是如此,还可以指定变量可以是多个变量类型:let gender = string | boolean;
;
any
表示任意类型。一个变量指定类型为 any,相当于对该变量关闭了 TS 的类型检测,若声明变量(不赋值)不指定类型,则 TS 解析器会自动指定变量的类型为 any(隐式的 any);
unknown
实际上是一个安全的 any。和 any 类型变量相比,unknown 类型的变量同样可以是任意类型,不过any 类型的变量可以直接赋值给其它类型的变量(编译不会报错,但是这样操作是不安全的),unknown 类型的变量不能直接赋值给其它类型的变量(可通过 类型断言 赋值给其它类型的变量);
void
空值(undefined),常用于表示函数返回值类型为 null、undefined
;
never
没有值,常用于表示函数没有返回值,即函数不能执行完,执行过程必须中断(例如抛出异常);
object
使用方式和其它类型类似,不同的是,在 JS 中数组、函数、{} 等都是 object 类型,以下代码都能成功编译:
let _var: object;
_var = [1, 2, 3];
_var = {};
_var = (num1, num2) => num1 + num2;
以上代码说明在指定类型为 object 时并没有很好的效果,不过比较有用的是结合字面量类型限制对象的属性:
// obj1 变量存储的对象有且只能有一个值类型为 string 的 name 属性
let obj1: { name: string };
obj1 = { name: '张三' };
// 在属性名后面加上 ?,表示该属性是可选的(obj2 变量存储对象包含该属性是可选的)
let obj2: { name: string; age?: number };
obj2 = { name: '李四' };
obj2 = { name: '李四', age: 22 };
// 通过以下语法,表示 obj3 对象必须包含 name 属性,其它的随意
let obj3: { name: string; [propName: string]: any };
obj3 = { name: '王五', age: 22, sex: 'male' };
Fuction
指定变量类型为函数(Function)。类似地,给直接指定变量类型为 Function 也没有太多意义,最主要的是限制函数的结构,语法和限制 object 类似。
// 类似地,也可以指定函数结构声明:
// 语法:(形参:类型, 形参:类型...) => 返回值类型
let sum: (num1: number, num2: number) => number;
// 形参名不一定保持一致,即 num1、num2变量名不要求保持一致
sum = (n1, n2) => n1 + n2;
Array
// 数组的类型声明:
// 类型[]
// Array<类型>
let arr1: string[];
arr1 = ['香蕉', '苹果', '梨'];
let arr2: Array<string>;
arr2 = ['香蕉', '苹果', '梨']
tuple(元组)
// 元组:固定长度的数组
// 语法:[类型, 类型, 类型...]
let tuple1: [string, string, string];
tuple1 = ['20173101', '张三', 'male'];
enum(枚举)
// 定义一个枚举
enum Gender {
Male = 0,
Female = 1
}
// 指定变量类型为枚举
let sex: Gender;
sex = Gender.Male;
& 操作符
// & 表示同时
let obj: { name: string } & { gender: number };
obj = { name: '张三', gender: 1 };
类型别名
// 定义类型别名
type stuInfo = { name: string } & { gender: number };
let obj: stuInfo;
obj = { name: '李四', gender: 0 };
类型断言
语法:
变量 as 类型
or
<类型>变量
通过类型断言,可以将 unknown
类型变量赋值给其它类型变量,如果类型 unknown 类型变量值和其它类型不匹配也不会报错【感觉会埋下隐患】。
let variable: unknown;
variable = '张三';
let uName: string = variable as string;
variable = 22;
let uAge: number = <number>variable;
TS 编译选项
对某个文件进行监视
# 对 index.ts 文件进行监视,-w 就是监听监视的意思(watch);
# 如果文件发生改变,就会自动进行编译
tsc index -w
通过 Ctrl + C
命令关闭监视;通过此方式,如果要监视多个文件,这意味着要开启多个命令行窗口,对不同的文件进行监视,这显然不是很方便的。
对多个文件进行编译和监视
若要同时对多个文件进行编译、监视,那么需要添加 TS 编译器的配置文件 tsconfig.json
文件(为 {}
就可以了)并执行以下命令:
# 对指定目录下的所有 .ts 文件进行编译
$ tsc
# 对指定目录下的所有 .ts 文件进行监视
$ tsc -w
配置tsconfig.json
tsconfig.json
是 TS 编译器的配置文件,TS 编译器可以根据它的信息来对代码进行编译【在路径中,**
表示任意目录,*
表示任意文件】。
配置项:
-
"include":用来指定哪些目录需要被编译(相对于 tsconfig.json 文件而言)【Array】;
-
"exclude":用来指定需要排除编译的目录【Array】;与 "include"通常只需要使用其中一个就可以了,默认值为 ["node_modules", "bower_components", "jspm_packages"];
-
"extends":定义被继承的配置文件;"extends": "./config.base":表示当前配置文件会自动包含 config目录下 base.json 中所有配置信息;
-
"files":指定被编译文件的列表,只有需要被编译的文件少时才会用到【Array】;
-
"compilerOptions":编译选项是配置文件中非常重要也比较复杂的配置选项【Object】;
- "target":用来指定 TS 被编译为 ES 的版本;可选值为: 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext',默认为 'es3';
- "module":用来指定要使用的模块化规范;可选值为:'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'esnext'【'es6' <=> 'es2015'】;
- "lib":用来指定项目中要使用的库,在前端页面开发时通常都不修改,在使用 Node 时,通常需要修改【可通过乱写一个运行,通过报错结果查看可填写的值】【Array】;
- "outDir":用来指定编译后文件所在目录;通常为 "./dist";
- "outFile":将代码合并为一个文件,设置 outFile 后,所有的全局作用域中的代码会合并到同一个文件中;
- "allowJs":是否对 JS 文件进行编译(特别是在设置编译输出目录时),默认为 false;
- "checkJs":是否检查 JS 代码是否符合 TS 语法规范(类型赋值必须一致),默认为 false;
- "removeComments":编译时,是否移除注释,默认为 false;
- "noEmit":不生成编译后的文件,即是否只执行编译,检查 TS 文件的语法,不生成 JS 文件,默认值为 false;
- "noEmitOnError":编译当有错误时,不生成编译后的文件,默认值为 false;
编写代码具体的设置:
- "alwaysStrict":用来设置编译后的文件是否使用严格模式,默认为 false;
- "noImplicitAny":不允许使用隐式的 any 类型,默认为 false;
- "noImplicitThis":不允许使用不明确类型的 this,默认为 false;
- "strictNullChecks":严格地检查空值,默认为 false;
- "strict":上面 4 个严格代码检查的总开关;
webpack 打包 TS 代码
安装 webpack 和 TS 相关包
# 快速生成 package.json 文件
$ npm init -y
# -D 表示 --save-dev,与之对应的 -S 表示 --save
$ npm i -S webpack webpack-cli typescript ts-loader
添加 webpack 配置文件 webpack.config.js
// 引入路径包
const path = require('path')
// webpack 中的所有配置信息都应该写在 module.exports 中
module.exports = {
// 指定入口文件
entry: './src/index.ts',
// 指定打包文件所在目录
output: {
// 指定打包文件的目录
path: path.resolve(__dirname, 'dist'), // 当前目录下创建 dist 文件夹
// 设置打包后的文件名
filename: 'bundle.js'
},
// 指定 webpack 打包时要使用的模块
module: {
// 指定要加载的规则
rules: [{
// test 指定的是规则生效的文件
test: /\.ts$/,
// 要使用的 loader
use: 'ts-loader',
// 要排除的文件
exclude: /node_modules/
}]
}
}
添加 ts 配置文件 tsconfig.json
{
"compilerOptions": {
"module": "es2015",
"target": "es2015",
"strict": true
}
}
webpack 插件之 html-webpack-plugin
对于 html-webpack-plugin 插件,其作用就是根据打包生成的 .js、.css
文件,自动生成引用这些文件的 .html
文件。
安装:
$ npm i html-webpack-plugin -D
基础配置 (in webpack.config.js):
// ...
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
// 配置 webpack 插件
plugins: [
new HtmlWebpackPlugin()
]
}
使用 html 模板 (in webpack.config.js):
// ...
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
// 配置 webpack 插件
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
webpack 服务器(webpack-dev-server)
webpack-dev-server 能根据代码改变而自动刷新页面,和 HBuilderX 内置服务器类似;不过不同的是,就算是修改 TS 文件后,也能自动监听并编译,刷新网页。
安装:
$ npm i webpack-dev-server -D
在 package.json 文件 scripts 属性中添加如下:
"scripts": {
"start": "webpack serve --open chrome.exe"
}
使用 npm run start
命令通过 Chrome 浏览器运行 webpack 服务器上的网页,从而达到修改代码,网页自动响应的效果。
webpack 插件之 clean-webpack-plugin
事实上,对于 dist
文件夹下打包的文件,每次打包时都是生成的新文件覆盖旧文件,在某些情况下可能出现旧文件遗留的情况,这样不是好。clean-webpack-plugin 插件的作用就是每次打包文件时都会先清除掉旧文件。
安装:
$ npm i clean-webpack-plugin -D
配置 (in webpack.config.js):
// ...
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...
// 配置 webpack 插件
plugins: [
// ...
new CleanWebpackPlugin()
]
}
webpack 设置引用模块
默认地,在 JS、TS 文件模块之间的互相引用,webpack 打包时并不认识,通过以下配置,使 webpack 认识到哪些模块是可以引用的 (in webpack.config.js):
// ...
module.exports = {
// ...
// 设置引用模块
resolve: {
extensions: ['.ts', '.js']
}
}
浏览器兼容插件之 babel
对于 TS 来说,本身 tsconfig.json
文件已经能够将 TS 编译成指定版本的 JS 文件,但是在浏览器兼容方面,这是远远不够的,需要用到 babel 这个第三方模块。
安装:
$ npm i @babel/core @babel/preset-env babel-loader core-js -D
配置 (in webpack.config.js):
// 指定 webpack 打包时要使用的模块
module: {
// 指定要加载的规则
rules: [{
// test 指定的是规则生效的文件
test: /\.ts$/,
// 要使用的 loader, babel 同样也需要对此进行加载
use: [{
loader: 'babel-loader', // 指定加载器
options: { // 设置 babel
presets: [ // 设置预定义环境
[
"@babel/preset-env", // 指定环境插件
// 配置信息
{
targets: { // 需要适配的浏览器版本
"ie": "8"
},
"corejs": "3", // 指定 corejs 的版本
"useBuiltIns": "usage" // 使用 corejs 的方式,"usage" 表示按需加载
}
]
]
}
},
'ts-loader' // 相对位置越靠后,越先执行
],
// 要排除的文件
exclude: /node_modules/
}]
}
面向对象
实例属性
// 定义 Person 类
class Person {
name: string;
gender: string = 'male';
}
静态属性
class Login {
static State: boolean = false;
}
// 使用
console.log(Login.State)
只读属性
// 定义 Person 类
class Person {
// 有类型自动判断,不需要写 name: string 也可以
readonly name = '张三';
}
值得注意的是:readonly 关键字也可以用在静态属性上。
实例方法
// 定义 Person 类
class Person {
// 定义实例属性
name: string = '张三';
gender: string = '男';
// 定义实例方法
sayHello() {
console.log('大家好');
}
}
const person = new Person();
person.sayHello();
静态方法
// 定义 Person 类
class Person {
// 定义实例属性
name: string = '张三';
gender: string = '男';
// 定义实例方法
static sayHello() {
console.log('大家好');
}
}
Person.sayHello();
构造函数
// 定义 Person 类
class Person {
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`大家好,我叫${this.name},今年${this.age}岁。`);
}
}
const zhangsan = new Person('张三', 22);
zhangsan.introduce(); // 大家好,我叫张三,今年22岁。
继承
// 定义动物类
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log('动物在叫');
}
}
// 定义猫类,并继承动物类
class Cat extends Animal {
// 重写 sayHello 方法
sayHello() {
console.log('喵喵喵~~~');
}
}
// 定义狗类,并继承动物类
class Dog extends Animal {
// 重写 sayHello 方法
sayHello() {
console.log('汪汪汪~~~');
}
}
// 实例化阿猫、阿狗
const cat = new Cat('猫咪', 2);
const dog = new Dog('旺财', 4);
console.log(cat); // Cat { name: '猫咪', age: 2 }
console.log(dog); // Dog { name: '旺财', age: 4 }
cat.sayHello(); // 喵喵喵~~~
dog.sayHello(); // 汪汪汪~~~
super 关键字
// 定义动物类
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log('动物在叫~~');
}
}
// 定义狗类
class Dog extends Animal {
age: number;
constructor(name: string, age: number) {
// 如果子类写了构造函数,在子类构造函数中必须对父类构造函数进行调用
super(name);
this.age = age;
}
// 重写 sayHello 方法
sayHello() {
// 调用父类 sayHello 方法
// super.sayHello();
console.log('汪汪汪~~');
}
}
const dog = new Dog('旺财', 2);
dog.sayHello();
super
关键字,在子类中表示父类对象;如果子类中没有写构造函数,那么在创建子类对象时,会默认调用父类构造函数。
抽象类
// 定义动物抽象类
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
// 定义抽象方法
// 抽象方法使用 abstract 开头,没有方法体
// 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
abstract sayHello(): void;
}
// 定义狗类
class Dog extends Animal {
// 没有重写构造方法,默认会调用父类构造方法
// 重写 sayHello 方法
sayHello() {
console.log('汪汪汪~~~');
}
}
const dog = new Dog('旺财');
dog.sayHello();
抽象类与其它类区别不大,只是不能用来创建对象,只能用来继承。
接口
// 定义基本信息接口
interface IBaseInfo {
name: string;
age: number;
sayHello(): void;
}
class Dog implements IBaseInfo {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log('汪汪汪~~~');
}
}
接口用来定义一个类结构,用来定义一个类应该包含哪些属性和哪些方法,同时接口也可以当成类型声明去使用。在定义时,所有的属性都不能有实际的值,所有的方法都没有方法体,接口只定义对象的结构而不考虑实际值。
属性的封装
和其它高级语言类似,TS 同样有 public(默认值)、protected、private
关键字修饰属性和方法,对属性而言,同样也有 getter、setter,和 C# 尤为相像,是同一个公司设计的语言。getter、setter 使用方式如下:
class Dog {
private _name: string;
constructor(name: string) {
this._name = name;
}
// getter、setter
get name() {
return this._name;
}
set name(value: string) {
this._name = value;
}
}
对于声明类的结构,还有一种简便的方式,即可以直接将属性定义在构造函数中:
class Dog {
constructor(public name: string, public age: number) {}
}
const dog = new Dog('旺财', 2);
console.log(dog.name, dog.age); // 旺财 2
泛型
// 在定义函数或者类时,如果遇到类型不明确就可以使用泛型
// 泛型指代字母是任意的,并没有明确规定是 T
function print1(data) {
console.log(data);
}
print1('hello, typescript'); // 不指定泛型,TS 可以自动对类型进行判断
print1(123); // 显示指定泛型,不容易出错
// 指定多个泛型
function print2(data1, data2) {
console.log(data1, data2);
}
print2('张三', 22);
// T extends IToString 表示泛型 T (数据 data)必须实现 IToString 接口
function print3(data) {
console.log(data);
data.introduce();
}
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`你好,我叫${this.name},今年${this.age}岁。`);
}
}
const lisi = new Person('李四', 20);
print3(lisi);
// 在类中同样也可以使用泛型
class MyClass {
constructor(info) {
this.info = info;
}
getType() {
console.log(typeof this.info);
}
}
const mc = new MyClass('张三');
mc.getType();
贪吃蛇案例
项目环境搭建(基于 webpack)
添加 package.json (使用 Node 管理包)
# 快速生成 package.json
$ npm init -y
安装 webpack 、ts 相关包
$ npm i webpack webpack-cli typescript ts-loader -S
添加 tsconfig.json
{
"compilerOptions": {
"module": "es6",
"target": "es3",
"removeComments": true,
"strict": true,
"noEmitOnError": true
}
}
安装 html-webpack-plugin、clean-webpack-plugin 等 webpack 插件
$ npm i html-webpack-plugin clean-webpack-plugin webpack-dev-server -D
安装项目兼容工具 babel
$ npm i @babel/core @babel/preset-env babel-loader core-js -D
添加 webpack.config.js,对以上内容配置如下
// 引入路径包
const path = require('path')
// 引入 webpack 插件
const HtmlWebpackPlugin = require('html-webpack-plugin') // 以某个 .html 文件为模板,生成引用 TS 生成 .js 文件的 .html 文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 每次 TS 生成 dist 文件夹时,都清空 dist 文件夹下的文件, 做到旧结果不干扰结果
// webpack 中的所有配置信息都应该写在 module.exports 中
module.exports = {
// 指定入口文件
entry: './src/index.ts',
// 指定打包文件所在目录
output: {
// 指定打包文件的目录
path: path.resolve(__dirname, 'dist'), // 当前目录下创建 dist 文件夹
// 设置打包后的文件名
filename: 'bundle.js',
// 告诉 webpack 不使用箭头函数
environment: {
arrowFunction: false
}
},
// 指定 webpack 打包时要使用的模块
module: {
// 指定要加载的规则
rules: [
{
// test 指定的是规则生效的文件
test: /\.ts$/,
// 要使用的 loader, babel 同样也需要对此进行加载
use: [
{ // 【再将 JS 代码通过 babel 转换】
loader: 'babel-loader', // 指定加载器
options: { // 设置 babel
presets: [ // 设置预定义环境
[
"@babel/preset-env", // 指定环境插件
// 配置信息
{
targets: { // 需要适配的浏览器版本
"ie": "8"
},
"corejs": "3", // 指定 corejs 的版本
"useBuiltIns": "usage" // 使用 corejs 的方式,"usage" 表示按需加载
}
]
]
}
},
'ts-loader' // 相对位置越靠后,越先执行【先将 TS 代码转换】
],
// 要排除的文件
exclude: /node_modules/
},
]
},
// 配置 webpack 插件
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
// 设置引用模块
resolve: {
extensions: ['.ts', '.js']
},
mode: "production" // 打包的环境,"development" | "production" | "none",开发|实际运用|无
}
当然,这只是对于 webpack 打包 TS 而言,还有其它内容暂未添加。
具体的,还是参照本文档的 snake 案例中 webpack.config.js 文件配置。
添加 less 处理模块
$ npm i less less-loader css-loader style-loader -D
添加 css 兼容性处理(postcss)
$ npm i postcss postcss-loader postcss-preset-env -D
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。