TS简介
- ts基于javascript语言构建
- ts是js的超集,对js语法进行了扩展
- 可以在任何支持js的平台运行
- ts支持es的新特性,并且还新增了一些新的特性,例如一些api,工具,抽象类啊什么的
- ts支持js的类型,并且新增了一些其他类型(枚举类...)
- 有着丰富的配置选项
- ts通过配置可以编译成任意版本的js es6 es3等等
TS开发环境
- 下载并安装nodejs
使用npm全局安装typescript编译器
- 打开cmd或者powershell(管理员打开)
- 输入
npm i -g typescript
测试typescript
- 新建一个文件 hello.ts
- 打开cmd输入以下命令进行编译ts文件
tsc hello.ts
- 查看是否编译成功,会多一个hello.js文件
TS类型声明
- 类型
类型 | 描述 | 类型 | 描述 |
---|---|---|---|
number | 数字 | string | 字符串 |
boolean | 布尔值(true, false) | 字面量 | 限制变量的值就是该字面量的值 |
any | 任意类型 | unknown | 类型安全的any |
vold | 空值,没有值(undefined) | never | 没有值,不能是任何值 |
object | js对象 | array | js数组 |
tuple | 元组,ts新增类型,固定长度数组 | enum | 枚举,ts新增类型 |
- 先变量类型声明,再进行赋值
let a: number;
a = 10;
a = 20;
// a = 'string' // 代码会报错,不能将类型“string”分配给类型“number”
console.log(a);
- 类型声明和赋值同时进行(ts会自动判断变量类型)
let c = 10; // 相当于 let c: number = 10
c = 20;
c = 'string'; // 代码报错 不能将类型“string”分配给类型“number”
- 函数参数,函数返回值类型声明
function sum(a: number, b: number): number{
return a + b;
}
// const count = sum(10, 20, 30); // 代码报错:应有 2 个参数,但获得 3 个
const count = sum(10, 20);
console.log('count: ' + count);
- 字面量类型声明 --- 使用 “|” 连接多个类型
let e: 10 | 13;
e = 10;
e = 13;
// e = 12; // 不能将类型“12”分配给类型“10”
- 多类型声明 --- 使用 “|” 连接多个类型
let d: number | string;
d = 1;
d = 'string';
// d = false; // 代码报错:不能将类型“boolean”分配给类型“string | number”
- any和unknown类型
let f: any; // 显式any
// let f; // 隐式any ts会自动检测类型
f = 1;
f = false;
f = 'string';
let g: boolean;
g = f // any 类型的值 赋值给 其他类型的变量 不会发生报错
let h: unknown;
h = 2;
h = 'string';
h = true;
// g = h; // 代码报错:不能将类型“unknown”分配给类型“boolean”
// 我们自己知道 h 就是一个 boolean类型 ts检测器不知道 我们可以自己判断
if (typeof h === 'boolean') {
g = h; // 此时不会报错
}
// 或者使用 ts 类型断言 直接告诉解析器 变量的类型
g = h as boolean;
g = <boolean> h;
- void和never类型
// void 表示空值,该函数没有返回值或者返回值是undefined
function fn(): void{
// return 123 代码报错:不能将类型“number”分配给类型“void”
return undefined
}
// never 表示没有值,永远不会有值,没有终点
function fn1(): never{
throw new Error('报错了!') // 抛出错误 程序终止
}
- object和array类型
// object 类型
let a: object;
a = {};
a = function () {};
// 使用对象字面量声明类型 --- 可以指定对象中的属性以及类型
// 属性名后面加?表示该属性是可选的
let b: {name: string, age?: number};
// b = {}; //代码报错:类型“{}”缺少类型“{ name: string; age: 18; }”中的以下属性
b = {name: '张三', age: 18};
// b = {name: '张三', age: 18, a: 1, b: 2} 代码报错:只能指定类型中已经存在的属性
// 如果还想要其他属性,可以使用[propName: string]: any;
let c: {name: string, age?: number, [propName: string]: any};
c = {name: '李四', a: 1, b: 2};
// 使用箭头函数的形式,设置函数的结构类型
let f: (a: number, b: number) => number;
f = function (a, b) {
return a + b;
}
// f = function(a: string, b) { // 两个a的类型不符合
// return 'ss'; // 代码报错:返回值必须符合上述定义返回值类型
// }
// array 类型 --- 两种声明写法
// 1. 类型[] 2. Array<类型>
let g: string[]; // 字符串数组
g = ['f', 'g'];
// g = ['f', 'g', 1] // 代码报错:不能将类型“number”分配给类型“string”
let h: Array<number>; // 数字数组
- 元组类型 --- 固定长度的数组
// 元组类型 --- 固定长度的数组
// 语法:[string, string, number, ...]
let r: [string, number, boolean];
r = ['ss', 12, true]; // 长度符合,且,每个元素的类型也要符合
- 枚举类型 --- 枚举类
// enum枚举类型
// 和不同的js对象本质上没什么区别
// 对于开发者来说,相比于拿值类比较判断,枚举类型更加易懂
// 提高易读性和可维护性
// 语法:enum 枚举名称 {}
enum Gender {
male, // 默认male = 0
female // 默认female = 1
}
// let y: {name: string, gender: 0|1};
// console.log(y.gender === 1);
let y: {name: string, gender: Gender};
y = {name: '张三', gender: Gender.male}
console.log(y.gender === Gender.male);
- |表示或者 和 &表示且
// |表示或者 和 &表示且
let u: 1 | 2;
// 对象,既要满足第一个也要满足第二个,即name和age两个属性都要有
let i: {name: string} & {age: number};
- 类型别名 --- 如果一个类型特别长,可以写别名代替
type numType = 1 | 2 | 3 | 4
let o: numType;
let p: numType;
o = 1;
p = 2;
TS编译选项配置
- 自动编译ts文件(只能自动编译单个ts文件)
tsc index.ts -w // 通过-w指令,监听文件的变化,自动编译
- 通过ts配置文件(tsconfig.json)进行编译
- 项目目录下新建tsconfig.json文件
- 此时可直接使用
tsc -w
命令,自动编译并监听,项目所有ts文件 tsconfig.json常用配置项
include --- 包含要编译的文件目录以及文件
- ** 表示任意目录,*表示任意文件
- 写法:
"include": ["./src/**/*"]
src文件夹下的ts文件都要进行编译
exclude --- 排除不需要编译的文件目录和文件
- 默认值:["node_modules", "bower_components", "jspm_packages"]
- ** 表示任意目录,*表示任意文件
- 写法:
"exclude": ["./exclude/**/*"]
exclude文件夹下的ts文件不需要编译
extends --- 继承外部json配置文件
- 写法:
"extends": "./config/xxx.json"
- 写法:
files --- 小项目,或者编译的文件很少,才会使用到这个选项,单独配置要编译的文件
- 写法:
"files: ["./src/index.ts", "./src/home.ts"]"
- 写法:
- compilerOnSave --- 保存ts文件时,是否进行即时编译,布尔值 默认flase
- buildOnSave --- 保存ts文件时,是否对文件进行编译,以及其他文件的构建任务,布尔值 默认false
compilerOptions --- 编译器选项,如何编译ts文件,编译成什么版本都在这个选项中进行配置
target --- 指定要编译成那个版本的js
- 默认值:ES3
- 可接受:"ES3", "ES5", "ES6", "ES2015", "ES2016", "ES2017", "ES2018","ES2019", "ES2020", "ES2021", "ES2022", "ES2023", "ESNext"
module --- 指定编译成的js文件使用什么模块化规则
- 默认值:target为es3 |es5,默认则是CommonJS规则,否则默认是 ES6/ES2015规则
- 可接受:"CommonJS", "AMD", "System", "UMD", "ES6", "ES2015","ES2020", "ESNext", "None", "ES2022", "Node16", "NodeNext"
lib --- 指定运行环境所需要使用的库
- 默认值:浏览器的一些环境,如:DOM,ES5
- 可接受: "ES5", "ES6", "ES2015", "ES2015.Collection", "ES2015.Core", "ES2015.Generator", "ES2015.Iterable", "ES2015.Promise", "ES2015.Proxy", "ES2015.Reflect", "ES2015.Symbol.WellKnown", 等还有许多
- 写法:
"lib: ["ES6", "DOM"]"
- outDir --- 指定ts文件编译后所在的目录
- outFile --- 将编译后的代码合并到同一个文件(此时模块化规范得是amd|system),不常用
allowJs --- 是否对js文件也进行编译
- 默认值:false,不对js文件进行编译
checkJs --- 是否对js文件语法进行检查
- 默认值:false,不对js文件语法进行检查
removeComments --- 是否移除注释
- 默认值:false, 不移除注释
noEmit --- 不要生成编译文件
- 默认值:false,允许生成编译文件
noEmitOnError --- 不生成编译文件,如果文件存在错误的话
- 默认值:false,有错误,照样生成编译后的文件
strict --- 严格检查的总开关
- 默认值:false,关闭
alwaysStrict --- 启用js严格模式
- 默认值:false, 不开启js严格模式
noImplicitAny --- 不允许隐式的any类型
- 默认值:false,允许隐式的any类型
noImplicitThis --- 不允许不明确的this
- 默认值:false, 允许不明确的this
strictNullChecks --- 严格检查空值
- 默认值:false,不检查空值
- strictBindCallApply --- 严格检查call,apply,bind的参数列表
- strictFunctionTypes --- 严格检查function函数的类型
- strictPropertyInitialization --- 严格检查属性是否初始化
- noFallthroughCasesInSwitch --- 检查switch语句包含正确的break
- noImplicitReturns --- 检查函数没有隐式的返回值
- noUnusedLocals --- 检查未使用的全局变量
- noUnusedParameters --- 检查未使用的参数
- allowUnreachableCode --- 允许不可达代码
在webpack中使用ts
初始化项目
- 创建项目文件夹
- npm init -y 生成包管理文件package.json文件
- 下载项目依赖包
# webpack项目
npm i webpack webpack-cli webpack-dev-server html-webpack-plugin -D
# ts文件编译成js文件
npm i ts-loader typescript -D
# 使用bable对js兼容性做处理
npm i babel-loader @babel/core @babel/preset-env core-js -D
- 创建webpack.config.js配置文件
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 入口文件
entry: path.resolve(__dirname, 'src/main.ts'),
output: {
path: path.resolve(__dirname, 'dist'), // 输出文件目录
filename: 'bundle.js', // 输出文件名称
environment: {
arrowFunction: false, // 关闭webpack打包后的自调用箭头函数
},
clean: true, // 每次打包清空上次目录
},
// webpack-dev-server
devServer: {
host: '127.0.0.1',
port: 9223, // 端口号
open: true
},
module: {
// 处理ts文件
rules: [
{
test: /\.ts$/,
// use: "ts-loader",
use: [
{
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", {
"targets": {"ie": "11", "chrome": "58"},
corejs: "3",
useBuiltIns: "usage" // 按需加载js垫片 使浏览器支持最新api
}]
]
}
},
"ts-loader"
],
exclude: path.resolve(__dirname, 'node_modules')
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html')
})
],
optimization: {
minimize: true,
},
resolve: {
extensions: [".js", ".ts"] // 快捷导入 无需写后缀
},
mode: 'production' // development production
}
- 创建tsconfig.json配置文件
{
"compilerOptions": {
"target": "ES2015",
"module": "ES2015",
"strict": true
}
}
class 类
类的定义
class 类名 {
// 实例属性 通过实例.属性名调用
属性名: 类型 = 值
属性名: 类型 = 值
// 静态属性 通过类.属性名调用
static 属性名: 类型 = 值
// 只读属性 通过实例.属性名调用 不可修改
readonly 属性名: 类型 = 值
// 只读静态属性 通过类.属性名调用 不可修改
static readonly 属性名: 类型 = 值
// 构造函数
constructor(参数: 类型) {
this.属性名 = 参数
},
方法名() {
...
}
}
类的使用
class Dog {
// 定义属性
name: string
age: number
constructor(name: string, age: number) {
// 构造函数中进行赋值
this.name = name
this.age = age
}
say() {
console.log(this.name + ': 汪汪汪');
}
}
const dog1 = new Dog('小黑', 2)
const dog2 = new Dog('小白', 1.5)
console.log(dog1.name , dog1.age);
dog1.say()
dog2.say()
类的继承
- 使用extends关键字实现继承
- 如果要给子类添加新的属性,则必须要在子类构造函数中,使用关键字super类,调用一下父类构造函数,并传递参数,此时才可以在子类添加新的属性
class Animal {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
say() {
console.log(this.name + '在叫');
}
}
/*
使用 extends 实现类继承
如果在子类中写了constructor构造函数,必须要调用super()重写调用一下父类构造函数,否则父类构造函数不会生效
*/
class Dog extends Animal {
state: number
constructor(name: string, age: number, state: number) {
super(name, age)
// 给子类创建新的属性
this.state = state
}
// 方法重写
say() {
console.log(this.name + ': 汪汪汪!');
}
}
class Cat extends Animal{
// 方法重写
say() {
console.log(this.name + ': 喵喵喵!');
}
}
const dog = new Dog('小黑', 2, 0)
const cat = new Cat('小白', 1.5)
dog.say()
console.log(dog.state);
cat.say()
抽象类
- 使用 abstract class类定义的类,就是抽象类
- 抽象类不能用来创建对象,只能被继承
- 抽象类可以创建抽象方法,抽象方法不能有实现
- 子类必须要对抽象方法进行重写
(function(){
/**
* 抽象类 使用 abstract class类定义
* 抽象类不能用于创建对象,只能用来继承
* 抽象类可以创建抽象方法,抽象方法不能有实现,子类必须要对抽象方法进行重写
*/
abstract class Animal {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
abstract say(): void
}
class Dog extends Animal {
// 方法重写
say() {
console.log(this.name + ': 汪汪汪!');
}
}
class Cat extends Animal{
// 方法重写
say() {
console.log(this.name + ': 喵喵喵!');
}
}
const dog = new Dog('小黑', 2, 0)
const cat = new Cat('小白', 1.5)
dog.say()
cat.say()
})()
类的属性封装
类的三种属性修饰符
- public(默认值):属性可以在类,子类,实例对象中进行修改
- protected:属性,只能在类,子类中进行修改
- private: 私有属性,只能在类中进行修改
- 私有属性访问,就需要属性封装,类中提供修改和获取属性的方法,降低容错率,避免随意修改属性,使属性更加的安全
(function() {
class Person {
name: string; // 类,子类,实例对象中都可以进行修改
protected age: number; // 只能在类,子类中进行修改
private sex: number; // 只能在类中进行修改,私有属性
constructor(name: string, age: number, sex: number){
this.name = name;
this.age = age;
this.sex = sex;
}
getSex(){
return this.sex;
}
setSex(sex: number) {
if (sex !== 0 && sex !== 1) return // 可以对数据做一些限制,不能随意修改
this.sex = sex;
}
}
const p1 = new Person('张三', 18, 0);
// console.log(p1.age); // 代码报错:只能在类“Person”及其子类中访问
// p1.age = 20; // 代码报错:只能在类“Person”及其子类中访问
p1.name = '李四';
// p1.sex = 10; 属性“sex”为私有属性,只能在类“Person”中访问
console.log(p1.getSex());
p1.setSex(1);
console.log('修改后的p1:', p1);
})()
类的简化写法
- 直接在构造其中声明属性,需要添加修饰符
// class A {
// name: string;
// age: number;
// constructor(name: string, age: number){
// this.name = name;
// this.age = age;
// }
// }
class A {
constructor(public name: string, private age: number){
}
get _age(){
return this.age
}
}
console.log(new A('张三', 10));
console.log(new A('张三', 10)._age);
interface 接口
- 接口用来定义类的结构
- 接口可以当做类型来使用,可以进行类型声明
- 接口里的方法都是抽象方法
- 接口可以重复声明,相同接口会合并属性和方法
- 接口定义类的时候,使用implements关键字实现接口,类必须满足接口定义的结构
(function () {
type myType = { // 使用type定义类型,不能重复定义相同的type类,会报错
name: string,
age: number
}
// type myType = {
// hobby: Array<string>
// }
interface myInterface {
name: string;
age: number;
say():void // 抽象方法
}
interface myInterface { // 多次声明相同接口不会报错
hobby: string[];
}
// interface接口:定义类型声明
const obj: myType = {name: '张三', age: 18}
let obj2: myInterface
obj2 = {
name: '张三',
age: 18,
say() {
console.log('hi~');
},
hobby: ['打游戏']
}
// interface接口:定义类结构
class Person implements myInterface {
name: string;
age: number;
hobby: Array<string>;
constructor(name: string, age: number, hobby: Array<string>) {
this.name = name
this.age = age
this.hobby = hobby
};
say (): void {
console.log('hi~');
}
}
const p = new Person('张三', 18, ['健身'])
console.log(p);
})()
generic泛型
- 当遇到类型不明确,无法确定其类型的时候,此时就可以使用泛型
- 泛型名字可以随便起
- 在函数中
function fn<T>(a: T): T{
return a;
}
// 多个泛型
function fn2<T, K>(a: T, b: K): T{
console.log(b);
return a;
}
// 1. 直接调用
console.log(fn(1));
// 2. 指定泛型类型
console.log(fn<string>('hello'));
// 3. 指定多个泛型
console.log(fn2<number, string>(10, 'hello'));
- 在class类中
class D<T>{
name: T;
constructor(name: T){
this.name = name
}
}
console.log(new D<string>('张三').name);
泛型范围约束
- 使用interface接口约束
- extends使泛型继承interface接口
// 约束泛型范围 必须要有一个属性 age
interface inter{
age: number
}
function F<T extends inter>(a: T) {
return a
}
F({age: 10})
评论 (0)