TypeScript快速入门
Vue3完全拥抱ts,学习与掌握ts已是燃眉之急!
1、TypeScript编译器的安装
node
和typescript
版本对应关系如下
https://blog.csdn.net/zsh501736479/article/details/128489424
因为我电脑的node
版本是v10.12.0
所以使用npm
安装typescript
时要指定版本,如
npm install typescript@4.1.2 -g
验证安装是否成功,终端命令行输入tsc
即可,如
laizhenghua@laizhenhuadeAir bin % tsc
Version 4.1.2
Syntax: tsc [options] [file...]
...
# 输出一些说明信息就安装成功了
如何使用tsc
呢?tsc (typescript compile)
的作用就是将.ts
文件编译成js
文件,类似javac
命令。以至于可以让node
或者是浏览器直接执行,例如
dist.ts
export { }
let msg: string = 'hello typescript'
console.log(msg) // hello typescript
进行编译tsc ./dist.ts
后会得到dist.js
文件,这个文件就可以直接用node
执行,如
laizhenghua@laizhenghuadeMacBook-Air code % tsc ./dist.ts
laizhenghua@laizhenghuadeMacBook-Air code % node ./dist.js
hello typescript
2、TypeScript数据类型
1、常见的数据类型
类型 | 举例 | 描述 |
---|---|---|
number | 1,123,-45 | 任意数字 |
string | ‘hello’ | 任意字符串 |
boolean | true,false | 布尔值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值或者是undefined |
never | 没有值 | 不能是任何值 |
object | {} | 任意的js对象 |
array | [] | 任意的js数组 |
tuple | [2,3] | ts新增的数据类型也就是固定长度的数组 |
enum | enum {A, B} | 枚举,ts新增的数据类型 |
2、类型申明写法
// 1.先申明再赋值
let a: number = 1;
// 2.声明和赋值同时进行(ts中也有变量类型自动推断,推断出来后这个变量只能该类型的值)
let b = '123';
// b = 1 会报错
// 3.字面量或常量的申明
let a: 10;
a = 10; // 可以赋值
a = 11; // 报错
// 4.可以使用 | 来连接多个类型(联合类型)
let b: 10 | 11;
b = 10; // 可以赋值
b = 11; // 可以赋值
// 5.函数返回值
function sum(a: number, b: number): number {
return a + b;
}
// 6.对变量关闭类型检测(回归原始js)
let key: any; // 也可以写成 let key; 隐式声明any
key = 1;
key = true;
key = 'abc'
// 7.unknown类型也可以关闭类型检测
let key: unknown;
key = 1;
key = true;
key = 'abc'
// 8.unknown实际上就是一个类型安全的any 不能直接赋值给其他变量
let primary: string;
primary = key; // 报错 不能直接赋值给其他变量
// 9.类型断言
let primary: string;
primary = key as string; // 告诉解析器就是一个string
// primary = <string> key;
// 10.对象写法
let d1: {name: string};
// 强制要求对象的 key-value 结构保持一致
d1 = {name: 'alex'};
// d1 = {name: 'alex', age: 12} 报错
let d2: {name: string, age ? : number}; // ? 表示age属性是可选的有没有都可以
d2 = {name: 'alex'};
// 任意属性写法
let d3: {name: string, [propName: string]: any};
// 11.函数变量写法
let fn: (a: number, b: number) => number;
fn = function(a: number, b: number): number {
return a + b;
}
// 12.数组变量写法(ts中要求数组中元素类型必须保持一致)
let list: string[];
list = ['1', '2'];
let dic: Array<string>;
dic = list;
// 13.元组(固定长度的数组)
let a: [string, string];
a = ['1', '2'];
3、为什么要加数据类型?消除一些安全隐患,让程序变得可控制
// js
function sum1(a, b) {
return a + b;
}
sum1(1, "123") // 不报错,存在一些不确定的安全隐患
// ts
function sum2(a: number, b: number) {
return a + b;
}
sum2(1, "123") // 报错信息:Argument of type 'string' is not assignable to parameter of type 'number'.
3、TypeScript编译选项
1、自动编译ts
文件
编译ts
文件时,使用-w
指令后,ts
编译器会自动监听文件的变化,并在文件发生变化时对文件进行重新编译,例如
tsc dist.ts -w
2、自动编译当前目录下的所有ts
文件
配合tsconfig.json
配置文件,可以编译当前配置文件所在目录下的所有ts
文件,例如
tsconfig.json
{
// 无需申明任何配置项,执行tsc 也能变异当前配置文件所在目录下的所有ts文件
}
3、tsconfig.json
的配置选项
tsconfig.json
是一个JSON文件,可以编写注释。添加此配置文件后,只需要tsc
命令即可完成对整个项目的编译,那个它有哪些配置选项,去控制编译行为?
/*
include
1.定义希望被编译的文件所在的目录
2.默认值 ["**/*"]
3.应用场景 项目中可能会有很多ts文件,我们不需要每个ts文件都编译,就可以使用include控制编译的目录或文件
exclude
1.排除不想编译的目录或者是文件
2.默认值 ["node_modules", "bower_components", "jspm_packages"]
extends
1.指定被继承的配置文件
2.了解即可,很少用到
files
1.指定被编译的文件,只有需要编译的文件很少时才需要用到
*/
{
// 只编译 src 目录下的任意ts文件
"include": [
"./src/**/*"
],
"exclude": [
"./src/test.ts"
]
}
// 以上配置项都比较简单 compilerOptions 编译选项是配置文件中非常重要也是比较复杂的配置选项
/*
compilerOptions 配置选项是一个对象
1.target 用来指定ts被编译为的ES的版本 默认是ES3
2.module 指定要使用的模块化的规范 可选值有 "CommonJS", "AMD", "System", "UMD", "ES6", "ES2015", "ES2020", "ESNext", "None", "ES2022", "Node16", "NodeNext"
3.lib 用来指定项目中要使用的库(一般不用管这个配置项)
4.outDir 用来指定编译后文件所在的目录
5.outFile 将全局作用域中的代码(编译后的代码)合并到一个文件中
6.allowJs 是否对js文件进行编译 默认是 false
7.checkJs 是否检查js代码是否符合语法规范 默认是 false
8.removeComments 编译时候是否移除ts代码中的注释
9.noEmit 不生成编译后的文件(检查ts语法)
10.noEmitOnError 编译时当有错误时不生成编译后的文件
11.alwaysStrict 用来设置编译后的文件是否使用严格模式
12.noImplicitAny 是否不允许出现隐式的 any 类型
13.noImplicitThis 是否不允许出现隐式的 this 指向(不明确的this)
14.strictNullChecks 是否开启严格检查空值
15.strict 所有严格检查的总开关(就不用一个个声明为true)
*/
{
"include": [
"./src/**/*"
],
"compilerOptions": {
"target": "ES2015",
// "module": "ES2015",
"outDir": "./dist",
"outFile": "./dist/app.js",
"allowJs": false,
"checkJs": false,
"removeComments": false,
"noEmit": false,
"noEmitOnError": false,
"alwaysStrict": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strict": true
}
}
4、TypeScript使用weback打包
实际开发中我们很少自己编写tsconfig.json
然后直接使用ts
编译器编译代码。一般都是借助打包工具,比如说webpack
。webpack
有完整的入口、出口、插件等功能,对代码分割、模块化、去掉死亡代码、提取公共代码也更加完善。
1、初始化测试项目,得到package.json
文件,在此文件中引入webpack
相关依赖。
# 初始化
laizhenghua@laizhenghuadeMacBook-Air ts % npm init -y
# 安装相关依赖(因为node版本是v10.12.0,需要指定版本号)
laizhenghua@laizhenghuadeMacBook-Air ts % npm install - D webpack@5.6.0 webpack-cli@4.2.0 typescript@4.1.2 ts-loader@8.0.11 @types/node@14.0.4
2、编写webpack
配置文件webpack.config.js
webpack.config.js
const path = require('path');
// webpack所有配置信息都写在 module.exports = {}
module.exports = {
// 指定入口文件
entry: './src/index.ts',
// 指定打包文件所在的目录(出口)
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'dist.js'
},
// 指定webpack打包是要使用的模块
module: {
// 指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
use: 'ts-loader',
// 排除的模块
exclude: /node_modules/
}
]
}
}
3、编写tsconfig.json
配置文件
tsconfig.json
{
"compilerOptions": {
"outDir": "./",
"module": "ES2015",
"target": "ES2015",
"strict": true
},
"exclude": [
"node_modules",
"dist"
]
}
4、最后我们只需添加编译命令即可
package.json
{
"name": "ts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// 注意添加的是这一行
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "^8.0.11",
"typescript": "^4.1.2",
"webpack": "^5.6.0",
"webpack-cli": "^4.2.0",
"@types/node": "^14.0.4"
}
}
5、测试(检查输出的文件)
以上就是整个ts
项目编译、打包过程。然而要想编译得到的js
文件可以在浏览器中执行,我们还需将html
文件也一起打包~
以下为webpack
扩展部分:
1、安装html-webpack-plugin
,用于自动生成html
文件
laizhenghua@laizhenghuadeMacBook-Air ts % npm install html-webpack-plugin@4.5.0 --save-dev
2、配置插件
webpack.config.js
const path = require('path');
// 引入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack所有配置信息都写在 module.exports = {}
module.exports = {
// 指定入口文件
entry: './src/index.ts',
// 指定打包文件所在的目录(出口)
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'dist.js'
},
// 指定webpack打包是要使用的模块
module: {
// 指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
use: 'ts-loader',
// 排除的模块
exclude: /node_modules/
}
]
},
// 配置webpack插件
plugins: [
new HtmlWebpackPlugin({
title: "TypeScript Test",
// template: "./src/index.html" 使用模版构建
})
],
// 用来设置引用模块
resolve: {
// ts和js都可以做为模块在其他模块中引入
extensions: ['.ts', '.js']
}
}
5、TypeScript面向对象
面向对象是程序中一个非常重要的思想,很多初学者理解起来也非常困难,这是一个比较深奥的问题,其实不然简单理解就是对象
就是程序中的一切,程序中所有的操作都需要通过对象来完成。
js
中常见对象有:
- 可以使用
window
对象操作浏览器,例如window.open('https://www.baidu.com')
- 可以使用
document
对象操作网页,例如document.createElement('a')
- 可以使用
console
对象操作控制台,例如console.log('hello world')
然而面向对象,三言两语根本解释不了,需要自己在漫长的学习与使用中慢慢领悟。
5.1、class(类)
前面我们提到,面向对象中对象就是程序的一切!要想创建或实例化对象,必须先有类,类就是对象的模型或者是模版(相当于告诉CPU在内存中要创建怎样的数据结构),基于这个模版才能实例化出对象。
ts
中定义一个类也很简单,如下:
export { }
// 定义一个Person类
class Person {
// 普通属性 属性名: 数据类型
name: string;
age: number;
gender: number;
// 静态属性(不用实例化对象也能访问可以通过 Person.handNum 访问)
static handNum: number = 2;
// 只读属性(不能被修改类似Java中的final关键字)
readonly eyes: number = 2;
// 静态只读属性
static readonly foots: number = 2;
// 声明构造方法
constructor(name: string, age: number, gender: number) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 定义方法
eat() {
console.log('人会吃');
}
// 静态方法
static run() {
console.log('人会跑')
}
}
const person = new Person('alex', 22, 0);
console.log(person);
console.log(Person.foots);
Person.run();
5.2、extends(继承)
一个新类从已有的类获得其已有的特性(获取其特性的同时还可以对新类进行扩展),称之为类的继承。继承也有很多好处!减少代码的冗余,提高代码的复用性,便于功能的扩展。
注意子类继承父类后,子类拥有了父类的所有结构。
export { };
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
eat(): void {
console.log('吃饭');
}
}
// ts中继承的关键字也是 extends
class ChinesPerson extends Person {
// 方法的重写
eat(): void {
console.log('用筷子吃饭')
}
// 对 Person类 进行扩展
run(): void {
console.log('人会走路');
}
}
const person = new Person('alex', 22);
person.eat(); // 吃饭
const chinesPerson = new ChinesPerson('马超', 22);
chinesPerson.eat(); // 用筷子吃饭
chinesPerson.run(); // 人会走路
ts
中是单继承还是多继承?
ts
中如何使用super
关键字?
同其他语言一样ts
中super
也是代表父类,可以在子类中调用父类的方法。
export { };
class Person {
// 属性可以任意修改将会导致对象中的数据变得非常不安全,因此将属性私有化是最好的
private name: string;
private age: number;
public static eyes: number = 2;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
eat(): void {
console.log('吃饭');
}
public getName(): string {
return this.name;
}
public setName(name: string): void {
this.name = name;
}
public getAge(): number {
return this.age;
}
public setAge(age: number): void {
this.age = age;
}
}
class ChinesPerson extends Person {
// 如果在子类中申明构造方法 必须先手动调用父类的构造方法
constructor(name: string, age: number) {
super(name, age);
this.setName(name);
}
// 方法的重写
eat(): void {
super.eat();
// 虽然是调用父类的获取属性方法,但是属性还是子类的
console.log('super name = ' + super.getName()); // super name = 马超
console.log('用筷子吃饭');
}
// 对 Person类 进行扩展
run(): void {
console.log('人会走路');
}
}
const person = new Person('alex', 22);
person.eat(); // 吃饭
console.log(person.getName());
const chinesPerson = new ChinesPerson('马超', 22);
chinesPerson.eat(); // 用筷子吃饭
chinesPerson.run(); // 人会走路
5.3、abstract class(抽象类)
const person = new Person('alex', 22);
对于Person类
来说泛指各种各样的人范围太大了,所以Person类
做为超类我们不希望以超类来创建对象,而是做为父类抽象一些属性与方法供其他类去继承与扩展,因此抽象类出现了。
被abstract
关键字修饰的类称为抽象类,抽象类不能被实例化,是用来被继承的,在抽象类中被abstract
修饰的方法叫抽象方法,抽象方法没有方法体,抽象类的子类需要重写父类的抽象方法。
export { };
abstract class Person {
// 抽象方法
public abstract eat(): void;
}
class Chines extends Person {
public eat(): void {
console.log('用筷子吃饭!');
}
}
5.4、interface(接口)
比抽象更抽象的结构就是接口!
接口就是规范、标准、契约,事先制定好一组规则,大家都要遵守!例如数据库驱动Driver
,Driver
是一个接口事先定义好了connect()、acceptsURL()
等没有方法体的方法,具体怎么连接数据库需要不同的厂商自己去实现(MySQL、Oracle等需要实现Driver
接口,重写connect()
方法实现数据库的连接逻辑)。
然而在ts
中,接口可以定义一个类的结构(一个类中应该包含哪些属性与方法)
// p1只可以定义一次
type p1 = {
name: string;
age: number;
}
const p1_impl: p1 = {
name: 'alex',
age: 22
}
// p2可以定义多次前提是要使用interface修饰
// 接口只能定义对象的结构不考虑实际值
interface p2 {
name: string;
age: number;
}
// 将接口当成类型去使用
const p2_impl: p2 = {
name: 'alex',
age: 22
}
ts
中接口就是限制类的结构,定义的方法都只能是抽象方法
export { };
interface Person1 {
name: string;
eat(): void;
}
interface Person2 {
age: number;
run(): void;
}
// 单继承多实现
class Chines implements Person1, Person2 {
name: string;
age: number;
eat(): void {
console.log('开始干饭')
}
run(): void {
console.log('开发跑步')
}
}
6、TypeScript泛型
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。
这个类型参数将在使用时(例如继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实 际的类型参数,也称为类型实参)。
// 泛型函数
function fn<T>(param: T): T {
return param;
}
console.log(fn(10));
console.log(fn('abc'));
// 指定泛型
console.log(fn<boolean>(true));