TypeScript
简称TS,它是以JavaScript为基础构建的语言,是JavaScript的超集
(TS是JS的升级版),扩展了JavaScript并添加了类型,可以在任何支持JavaScript的平台中执行。
JavaScript是弱类型语言,很多错误只有在运行时才会被发现,而TypeScript提供了一套静态检测机制,可以帮助我们在编译时就发现错误 。
JS
是一门标准的弱类型且动态类型的语言,所以JS灵活多变,缺失类型系统的可靠性。
TS
是一门标准的强类型且静态类型的语言。
强类型VS弱类型
强类型
:在语言层面就限制了函数的实参类型必须与形参类型相同
弱类型
:语言层面不会限制实参的类型; 我们定义一个变量,不需要考虑它的类型
形参
相当于函数中定义的变量,实参
是在运行时的函数调用时传入的参数。
强类型语言的特点就是不允许程序在发生错误后继续执行
弱类型的一个特点就是在计算时,不同类型之间对使用者透明地对变量进行隐式转换。
相较于弱类型语言,强类型有着明显的优势
:
静态类型VS动态类型
静态类型语言
:一个变量在声明时它的类型就是明确的,声明过后,它的类型就不可修改;
动态类型语言
:在代码运行阶段才能明确变量的类型,而且变量的类型随时可以改变;
两者区别
:是否允许随时修改变量的类型 ,
如何区分
:一门语言在编译时报错,那么是静态类型,如果在运行时报错,那么是动态类型。
静态类型的好处
:有利于代码的重构,可以在编译器编译时捕获错误。这样我们在编写代码的时候就能避免很多错误,提高编码的效率!
静态类型语言 | 动态类型语言 |
---|---|
对类型极度严格 | 对类型非常宽松 |
立即发现错误 | 不能立即发现(单元测试) |
运行时性能好 | 运行时性能差 |
自文档化 | 可读性差(工具生成文档) |
安装Node
:https://nodejs.org/zh-cn/
安装ts
:
npm i -g typescript
将ts文件编译为js文件(生成一个和ts文件名称相同的js文件),然后执行js文件
//编译ts文件
tsc XXX.ts
//运行js文件
node XXX.js
也可以安装ts-node直接运行ts文件 ,不需要编译为js
/安装ts-node
npm i -g ts-node
//直接运行ts文件
ts-node XXX.ts
类型声明
给变量设置了类型,使得变量只能存储某种类型的值。
通过类型声明可以指定TS中变量(参数字、形参)的类型,指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错。
语法如下:
// 声明一个变量
let 变量: 类型;
// 声明一个变量并给其赋值
let 变量: 类型 = 值;
// 声明一个函数
function fn(参数: 类型, 参数: 类型): 函数返回值类型{
...
}
// a 的类型设置为了number,在以后的使用过程中a的值只能是数字
let a: number;
a = 10;
a = 33;
a = 'hello'; // 此行代码会报错,因为变量a的类型是number,不能赋值字符串
自动类型判断
:TS拥有自动的类型判断机制,当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型,所以变量的声明和赋值时同时进行可以省略掉类型声明。
注
:虽然可以省略类型,但声明类型对定义函数有极大的提升
let a = 1;
a = 'sj';//报错 不能将类型'string'分配给类型'number'
类型 | 例子 | 描述 |
---|---|---|
number | 1, -33, 2.5 | 任意数字 |
string | ‘hi’, “hi”,模板字符串`` | 任意字符串 |
boolean | true、false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值(或undefined) |
never | 没有值 | 不能是任何值 |
object | {name:‘孙悟空’} | 任意的JS对象 |
array | [1,2,3] | 任意JS数组 |
tuple | [4,5] | 元组,TS新增类型,固定长度数组 |
enum | enum{A, B} | 枚举,TS中新增类型 |
number
:任意
let a: number;
// a 的类型设置为了number,在以后的使用过程中a的值只能是数字
a = 10;
a = 33;
// a = 'hello'; // 此行代码会报错,因为变量a的类型是number,不能赋值字符串
string
:任意字符串
let color: string = "blue";
color = 'red';
boolean
:布尔值true或false
let isDone: boolean = false;
字面量
:字面量不仅可以表示值,还可以表示类型,即所谓的字面量类型。也可以使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围。
let a: 10; //a的值只能为10
//可以使用|来连接多个类型(|为联合类型表示取值可以为多种类型中的一种)
let b: 'male' | 'female'; //b的值只能二选一
b = 'male';
b = 'female';
let c: boolean | string;
c = true;
c = 'sd ';
any
:任意类型
一个变量设置为any后相当于对该变量关闭了TS的类型检测,故不建议使用。声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any(隐式的any)。
let d: any = 4;
d = 'hello';
let e ;
e=1;
e=true;
//d的值为any,它可以赋值给任意变量
let s:string
s=d;//这样变量s的类型检测也关闭了
unknown
:类型安全的any,不能直接赋值给其他变量
let e: unknown;
e="hello"
let f:string
f=e;//报错 类型unkonwn不能赋值给string类型
void
:void表示没有任何类型,和其他类型是平等关系,不能直接赋值:
声明一个void类型的变量没有什么大用,我们一般也只有在函数没有返回值时去声明。
//void用来表示空
function fn(): void {
return undefined//除了undefined的其他类型的返回值会报错
}
never
:不能是任何值
//never 表示永远不会返回结果 连undefined都不能返回,一般用来报错
function fn1(): never {
throw new Error('报错了');
}
object
:任意的JS对象
//1.定义对象结构的类型声明
/*
语法:{属性名:属性值,属性名:属性值}
在属性名后边加上?,表示属性是可选的
*/
let b: { name: string; age?: number };
b = { name: '孙悟空', age: 18 };
//[xxx:string]:any 表示任意类型的属性
let c: { name: string; [propName: string]: any };
c = { name: '猪八戒', age: 18, gender: '男' };
//2.定义函数结构的类型声明
//我们一般会限制函数有几个参数,参数的类型,返回值的类型,可以以一种类似箭头函数的方式来声明一个函数类型的变量
/*
语法:(形参:类型,形参:类型...) => 返回值
*/
let d: (a: number, b: number) => number;
d = function (n1, n2): number {
return n1 + n2;
};
array
:任意JS数组
/*
语法:类型[]或Array<类型>
*/
//如string[]表示字符串数组 (在元素类型后面接上[]) Array表示数字数组
let e: string[]
//or
let e: Array<string>;
tuple
: 元组,TS新增类型,就是固定长度(数量)数组
元组中包含的元素,必须与声明的类型一致,数量要对应,且顺序也要一一对应
/*
语法:[类型,类型,类型]
*/
let h: [string,string,number];//里面两个字符串类型,一个数字
h = ['s', 'f',1];//必须对应两个字符串,一个数字,顺序也不能变化
比如我们在定义一个对象的时候 let person = { name: ‘zs’, gender: ‘male’ }。像gender
这种属性的取值一般只有两个,所以在存储的时候用数字来代替,可以提高效率,可以定义0
表示女性,1
表示男性
enum
:枚举,TS中新增类型,把所有可能的情况全部列出来,适合用于多个值进行选择的场景,
//enum:结果是多个值进行选择
enum Sex {
Male,
Female,
}
let i: { name: string; sex: Sex };
i = {
name: '张三',
sex: Sex.Male,
};
console.log(i.sex === Sex.Male);//true
联合类型
:表示取值可以为多种类型中的一种,使用 |
分隔每个类型。
let h: string | number;
h = '张三';
h = 18;
交叉类型
:是将多个类型合并为一个类,使用&
,表示同时
let j: { name: string } & { age: number };
j = { name: '张三', age: 18 };
类型别名
:用来给一个类型起个新名字。 仅仅是给类型取了一个新的名字,并不是创建了一个新的类型,类型别名常用于联合类型
type Message = string | string[];
let greet = (message: Message) => {
// ...
};
类型断言
:有些情况下,变量的类型对于我们来说是很明确,但TS编译器并不清楚,可以通过类型断言来告诉编译器变量的类型,断言有两种形式:
//类型断言,可以用来告诉编译器变量的实际类型
/*
语法: (变量 as 类型) 或 < 类型 > 变量
*/
let e: unknown;
e='ss' //e实际是字符串,但e的类型设置的是unkonwn
let f: string;
f=e//报错
f = e as string;//告诉ts编译器e就是字符串,这样就不会出现上述f=e报错的情况
// 或
f = <string>e;
编译文件时,使用 -w 指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译。
tsc xxx.ts -w
一个文件一个文件的编译太麻烦了,我们可以对整个项目进行编译
首先在项目根目录下创建一个ts的配置文件 tsconfig.json
,然后就可以使用tsc
指令,编译项目下的所有ts文件为js文件,当然也可以开启监视模式tsc -w
监视所有的文件
tsconfig.json配置总览
{
"compilerOptions": {
"target": "es5", // 指定 ECMAScript 目标版本: 'ES5'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"moduleResolution": "node", // 选择模块解析策略
"experimentalDecorators": true, // 启用实验性的ES装饰器
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
"sourceMap": true, // 把 ts 文件编译成 js 文件的时候,同时生成对应的 map 文件
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"alwaysStrict": true, // 以严格模式检查模块,并在每个文件里加入 'use strict'
"declaration": true, // 生成相应的.d.ts文件
"removeComments": true, // 删除编译后的所有的注释
"noImplicitReturns": true, // 不是函数的所有返回路径都有返回值时报错
"importHelpers": true, // 从 tslib 导入辅助工具函数
"lib": ["es6", "dom"], // 指定要包含在编译中的库文件
"typeRoots": ["node_modules/@types"],
"outDir": "./dist",
"rootDir": "./src"
},
"include": [
"./src/**/*.ts"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
]
}
*
表示任意文件 **
表示任意目录["**/*"]
"include":["src/**/*", "test/**/*"] // 所有src目录和test目录下的文件都会被编译
["node_modules", "bower_components", "jspm_packages"]
"exclude": ["./src/hello/**/*"] // src下hello目录下的文件都不会被编译
定义被继承的配置文件(和引入外部文件类似)
"extends": "./configs/base" // 自动包含configs目录下base.json中的所有配置信息
指定被编译文件的列表,只有需要编译的文件少时才会用到(和include类似)
列表中的文件都会被TS编译器所编译
"files": [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"utilities.ts",
"binder.ts",
"checker.ts",
"tsc.ts"
]
重要的编译选项,在compilerOptions中包含多个子选项,用来完成对编译的配置
target
:设置ts代码编译的目标版本
对于选项有哪些可选值,我们可以随便写一个值,编辑器会提示我们有哪些可选值
"compilerOptions": {
//可选值: “ES3”(默认), “ES5”, “ES6”, “ES2015”, “ES2016”, “ES2017”, “ES2018”, “ES2019”, “ES2020”, “ES2021”, “ESNext”.
"target": "ES6"//ts代码将会被编译为ES6版本的js代码
}
lib
:指定代码运行时所包含的库(宿主环境),一般运行在浏览器环境下的,就不需要自己单独设置这个
"compilerOptions": {
//可选值:“ES5”, “ES6”, “ES2015”...很多
"lib": ["ES6", "DOM"]
}
module
:设置编译后代码使用的模块化系统
"compilerOptions": {
//可选值:“CommonJS”, “AMD”, “System”, “UMD”, “ES6”, “ES2015”, “ES2020”, “ESNext”, “None”, “es2022”, “node12”, “nodenext”
"module": "CommonJS"
}
outDir
:指定编译后文件的所在目录
"compilerOptions": {
"outDir": "./dist"//编译过的js文件将会生成到dist目录。可以将源码与编译后的代码分开存放
}
outFile
:将所有的文件编译为一个js文件
"compilerOptions": {
"outFile": "./dist/app.js"
}
rootDir
:指定代码的根目录,默认情况下编译后文件的目录结构会以最长的公共目录为根目录,通过rootDir可以手动指定根目录
"compilerOptions": {
"rootDir": "./src"
}
//是否对js文件编译,默认值:false
"allowJs":false,
//是否对js文件进行语法检查,默认值:false
"checkJs":false
//是否删除注释,默认值:false
"removeComments":false
//不生成编译后的文件,默认值:false
"noEmit":false
//当有错误的时候不生成编译后的文件,默认值:false
"noEmitOnError":false
//是否生成sourceMap,默认值:false
"remove":false
//是否启用所有的严格检查(总开关),设置true后相当于开启了所有的严格检查,后面的检查不需要在写了,默认值:false
"strict":false
//是否总是以严格模式对代码进行编译,默认值:false
"alwaysStrict":false
//是否允许隐式的 any 类型,默认值:false
"noImplicitAny":false
//是否允许隐式的 this,默认值:false
"noImplicitThis":false
//严格检查bind、call和apply的参数列表,默认值:false
"strictBindCallApply":false
//严格检查函数的类型,默认值:false
"strictFunctionTypes":false
//检查是否存在空值,默认值:false 可以用a?.b判断a是否是空值
"strictNullChecks":false
//严格检查属性是否初始化,默认值:false
"strictPropertyInitialization":false
//是否检查switch语句包含正确的break
"noFallthroughCasesInSwitch":false
//是否检查函数没有隐式的返回值
"noImplicitReturns":false
//是否检查未使用的局部变量
"noUnusedLocals":false
//是否检查未使用的参数
"noUnusedParameters":false
//检查不可达代码;true:忽略不可达代码,false:不可达代码将引起错误
"allowUnreachableCode":false
通常情况下,实际开发中我们都需要使用构建工具对代码进行打包;TS同样也可以结合构建工具一起使用,下边以webpack为例
介绍一下如何结合构建工具使用TS;
通过执行命令 npm init -y
初始化一个项目并创建package.json文件
使用tsc --init
创建ts的配置文件tsconfig.json
创建src/index.ts
文件,用来编写ts代码
安装以下依赖包
npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpack-plugin
webpack:构建工具webpack
webpack-cli:webpack的命令行工具
webpack-dev-server:webpack的开发服务器
typescript:ts编译器
ts-loader:ts加载器,用于在webpack中编译ts文件
html-webpack-plugin:webpack中html插件,用来自动创建html文件
clean-wepack-plugin:webpack中的清除插件,每次构建都会先清除目录
根目录下创建webpack的配置文件webpack.config.js
:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
// 指定入口文件
entry: "./src/index.ts",
// 开发模式使用,方便查错误
devtool: "inline-source-map",
// 配置服务器
devServer: {
contentBase: "./dist",
},
// 指定打包文件所在目录
output: {
path: path.resolve(__dirname, "dist"),
//打包后的文件
filename: "bundle.js",
environment: {
arrowFunction: false, // 关闭webpack的箭头函数,可选
},
},
// 用来设置引用模块
resolve: {
extensions: [".ts", ".js"],//以ts js结尾的文件都可以作为模块使用
},
// 配置webpack的loader(打包使用的模块)
module: {
//加载规则
rules: [
{
//规则生效的文件
test: /.ts$/,//以ts结尾的文件
//要使用的loader
use: {
loader: "ts-loader",
},
//排除的文件
exclude: /node_modules/,
},
],
},
// 配置webpack的插件
plugins: [
// 构建前清除目录
new CleanWebpackPlugin(),
//自动创建html文件
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
};
根目录下创建tsconfig.json,配置可以根据自己需要
{
"compilerOptions": {
"target": "ES2015",
"module": "ES2015",
"strict": true
}
}
修改package.json添加如下配置
{
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack serve --open chrome.exe"
},
...
}
除了webpack,开发中还经常需要结合babel
来对代码进行转换以使其可以兼容到更多的浏览器,通过以下步骤可以将babel引入到项目中。
安装以下依赖包
npm i -D @babel/core @babel/preset-env babel-loader core-js
修改webpack.config.js配置文件
这样修改后,使用ts编译后的文件将会再次被babel处理,使得代码可以在大部分浏览器中直接使用,可以在配置选项的targets中指定要兼容的浏览器版本。
module: {
rules: [
{
//test指定的是规则生效的文件
test: /.ts$/,
//要使用的loader
use: [
//配置babel
{
//指定加载器
loader: "babel-loader",
// 设置babel
options: {
// 设置预定义的环境
presets: [
[
// 指定环境的插件
"@babel/preset-env",
// 配置信息
{
// 要兼容的目标浏览器
targets: {
chrome: "58",
ie: "11",
},
// 指定corejs的版本
corejs: "3",
// 使用corejs的方式 "usage" 表示按需加载
useBuiltIns: "usage",
},
],
],
},
},
{
loader: "ts-loader",
},
],
exclude: /node_modules/,
},
];
}
在src下创建ts文件,并在并命令行执行npm run build
对代码进行编译;
或者执行npm start
来启动开发服务器;
以上是一些基本的配置,但是在实际开发中,webpack在配置开发环境与生产环境时,配置的有些东西不太相同,所以我们应该分开写我们生产环境和开发环境的webpack配置
所以我们就在根目录下创建build文件夹存放我们的webpack配置文件
安装webpack-merge
//基于公共配置通过webpack-merge合并开发或者生产环境的特有配置,生成完整的开发或者生产环境配置
npm i -D webpack-merge
基本配置webpack.base.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.ts",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
environment: {
arrowFunction: false, // 关闭webpack的箭头函数,可选
},
},
resolve: {
extensions: [".js", ".ts"],
},
module: {
rules: [
{
test: /.ts$/,
use: [
{
loader: "ts-loader",
},
],
exclude: /node_modules/,
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
};
开发环境配置webpack.dev.config.js
module.exports = {
devtool: "inline-source-map",
};
生产环境配置webpack.pro.config.js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [new CleanWebpackPlugin()],
};
配置主文件webpack.config.js
const { merge } = require("webpack-merge");
const baseConfig = require("./webpack.base.config");
const devConfig = require("./webpack.dev.config");
const proConfig = require("./webpack.pro.config");
module.exports = (env, argv) => {
let config = argv.mode === "development" ? devConfig : proConfig;
return merge(baseConfig, config);
};
index.ts
const box = document.querySelector('#app')
const hello: string = 'Hello TS'
if (box !== null) {
box.innerHTML = hello
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TS & webpack</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
修改package.json添加如下配置
{
......
"scripts": {
"start": "webpack-dev-server --mode=development --config ./build/webpack.config.js",
"build": "webpack --mode=production --config ./build/webpack.config.js"
},
......
}
在src下创建ts文件,并在并命令行执行npm run build
对代码进行编译,执行npm start
来启动开发服务器
面向对象(OOP Object-Oriented Programming)
是一种优秀的程序设计方法,它的基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。
对象
就是一个自包含的实体,用一组可识别的特性(属性)和行为(方法)来标识。程序中的对象就是对现实世界中具体实体的抽象。
一个事物到了程序中就变成了一个对象,在程序中所有的对象都被分成了两个部分:数据和功能,以人为例,人的姓名、性别等属于数据,人可以说话、走路这些属于人的功能。数据在对象中被称为属性,而功能就被称为方法。简而言之,在程序中一切皆是对象。
类
就是具有相同属性和功能的对象的抽象的集合,可以理解为对象的模型。一般是先创建 类
,然后通过实例化 类
来得到 对象
。
定义类
class Cat{
// 属性的类型声明,在TS中必须有初始值,或者在构造函数中初始化
name: string
age: number
// constructor构造函数会在对象创建时调用
constructor(name: string) {
// 初始化属性,this表示当前调用对象
this.name = name
this.age = 3
}
// 方法
sayHi(): void{
// 在方法中可以通过this表示当前调用方法的对象
console.log(`喵,我是 ${this.name}`)
}
}
使用类
let Tom = new Cat("Tom") // 创建Cat类的实例
console.log(Tom) // Cat: { "name": "Tom", "age": 3 } (实例上只有属性)
Tom.say() // "喵,我是Tom" (方法在原型上,所以调用可以通过原型链访问到)
继承(Inheritance
):在继承机制下形成有层级的类,使得低层级的类可以延用高层级类的特征和方法。
继承的实现方式:
实现继承
:直接使用基类公开的属性和方法,无需额外编码。接口继承
:仅使用接口公开的属性和方法名称,需要子类实现。继承的作用:
复用代码,减少类的冗余代码。
使得类与类之间产生关系,为多态的实现打下基础。
创建一个猫类的父类 Animal
class Animal{
name: string
age: number
construcor(name: string){
this.name = name
this.age = 3
}
say(){
console.log('...')
}
}
使用 extends
关键字来继承 Animal
类,在子类中使用 super
完成对父类的引用
class Cat extends Animal {
color: string
construcor(name: string, color: string){
super(name)//调用父类的构造函数
this.color = color
}
}
通过继承可以将其他类中的属性和方法引入到当前类中 (在不修改类的情况下完成对类的扩展)
class Animal{
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
}
// 继承自动物类
class Dog extends Animal{
// 增加新方法
bark(){
console.log(`${this.name}在汪汪叫!`);
}
}
const dog = new Dog('旺财', 4);
dog.bark();
发生继承时,如果子类中的方法会替换掉父类中的同名方法,这就称为方法的重写
class Animal{
name: string;
age: number;
constructor(name: string, age: number){
this.name = name;
this.age = age;
}
run(){
console.log(`父类中的run方法!`);
}
}
class Dog extends Animal{
bark(){
console.log(`${this.name}在汪汪叫!`);
}
// 重写父类的run方法
run(){
console.log(`子类中的run方法,会重写父类中的run方法!`);
}
}
const dog = new Dog('旺财', 4);
dog.bark();
抽象类(abstract class
)是专门用来被其他类所继承的类,它只能被其他类所继承不能用来创建实例
abstract class Animal {
eat() {
console.log('eat')
}
abstract sleep(): void
}
不能直接实例化抽象类,通常需要我们创建子类继承基类,然后可以实例化子类
class Dog extends Animal {
name: string
constructor(name: string){
super()
this.name = name
}
run(){}
sleep(){
console.log('dog sleep')
}
}
抽象类中可以添加抽象方法,使用abstract
开头的方法叫做抽象方法,抽象方法没有方法体只能定义在抽象类中,继承抽象类时抽象方法必须要实现
abstract class Animal{
abstract run(): void; // 抽象方法
bark() {
console.log('动物在叫~');
}
}
class Dog extends Animals{
run() {
console.log('狗在跑~');
}
}
接口(Interface)
是对行为的抽象,而具体如何行动需要由类(class)去实现(implement)
接口
主要负责定义一个类的结构,可以在定义类的时候去限制类的结构,其中所有的属性都不能有实际的值,所有的方法都是抽象方法。接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口。
示例(检查对象类型)
// 描述一个对象的类型,类型别名type
type myType = {
name: string,
age: number
};
// 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法,可以当成类型声明去使用
interface myInterface {
name: string;
age: number;
}
//接口可以重复声明,相当于一个接口中有name age gender三个属性
interface myInterface {
gender: string;
}
const obj: myInterface = {
name: 'sss',
age: 111,
gender: '男'
};
示例(实现)
// 接口可以在定义类的时候去限制类的结构,其中所有的属性都不能有实际的值,所有的方法都是抽象方法
interface myInter {
name: string;
sayHello(): void;
}
//定义类时,可以使类去实现一个接口,使类满足接口的要求
class MyClass implements myInter {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log('大家好~~');
}
}
对象实质上就是属性和方法的容器,它的主要作用就是存储属性和方法,这就是所谓的封装
,默认情况下,对象的属性是可以任意的修改的,为了确保数据的安全性,在TS中可以对属性的权限进行设置
封装的作用:
静态属性(static)
:也称为类属性。使用静态属性无需创建实例,通过类即可直接使用,静态属性使用 static
开头
只读属性(readonly)
:如果在声明属性时添加一个readonly,则属性便成了只读属性无法修改
示例
class Tools{
readonly a =10
static PI = 3.1415926;
}
const tool = new Tools();
tool.a//10 只读属性不可以修改
Tools.PI//静态属性可以直接获取,3.1415926
TS中属性具有三种修饰符:
public(默认值)
:修饰的属性可以在任意位置修改protected (受保护成员)
,可以在类或子类的内部修改private (私有成员)
:修饰的属只能在类的内部修改public修饰符示例
class Person{
// public可以不写
public name: string;
age: number;
constructor(name: string, age: number){
this.name = name; // 可以在类中修改
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}
}
class Employee extends Person{
constructor(name: string, age: number){
super(name, age);
this.name = name; //子类中可以修改
}
}
const p = new Person('孙悟空', 18);
p.name = '猪八戒';// 可以通过对象修改
protected修饰符示例
class Person{
protected name: string;
protected age: number;
constructor(name: string, age: number){
this.name = name; // 可以修改
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}}
class Employee extends Person{
constructor(name: string, age: number){
super(name, age);
this.name = name; //子类中可以修改
}
}
const p = new Person('孙悟空', 18);
p.name = '猪八戒';// 不能修改
private修饰符示例
class Person{
private name: string;
private age: number;
constructor(name: string, age: number){
this.name = name; // 可以修改
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}
}
class Employee extends Person{
constructor(name: string, age: number){
super(name, age);
this.name = name; //子类中不能修改
}
}
const p = new Person('孙悟空', 18);
p.name = '猪八戒';// 不能修改
对于一些不希望被任意修改的属性,可以将其设置为private
,直接将其设置为private
将导致无法再通过对象修改其中的属性。
那么我们可以在类中定义一组读取、设置属性的方法使得私有属性private可以被外部访问,这种对属性读取或设置的属性被称为属性存取器
读取属性的方法叫做setter
方法,设置属性的方法叫做getter
方法。
class Person{
private _name: string;
constructor(name: string){
this._name = name;
}
get name(){
return this._name;
}
set name(name: string){
this._name = name;
}
}
const p1 = new Person('孙悟空');
console.log(p1.name); // 通过getter读取name属性
p1.name = '猪八戒'; // 通过setter修改name属性
定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定),此时泛型
就可以表示这个类型。
举个例子
function test(arg: any): any{
return arg;
}
test函数有一个参数类型不确定,但是能确定的时其返回值的类型和参数的类型是相同的,由于类型不确定所以参数和返回值均使用了any,但是很明显这样做是不合适的,首先使用any会关闭TS的类型检查,其次这样设置也不能体现出参数和返回值是相同的类型
使用泛型
function test<T>(arg: T): T{
return arg;
}
这里的
就是泛型
,T是我们给这个类型起的名字(不一定非叫T),设置泛型后即可在函数中使用T来表示该类型。泛型就是表示某个类型。
如何使用上面的函数?
方式一(直接使用):
//使用时可以直接传递参数使用,类型会由TS自动推断出来,但有时编译器无法自动推断时还需要使用下面的方式
test(10)
方式二(指定类型):
//指定类型
test<number>(10)
可以同时指定多个泛型,泛型间使用逗号隔开:
function test<T, K>(a: T, b: K): K{
return b;
}
test<number, string>(10, "hello");
使用泛型时,完全可以将泛型当成是一个普通的类去使用,类中同样可以使用泛型
class MyClass<T>{
prop: T;
constructor(prop: T){
this.prop = prop;
}
}
除此之外,也可以对泛型的范围进行约束
interface MyInter{
length: number;
}
//使用T extends MyInter表示泛型T必须是MyInter的子类,不一定非要使用接口类和抽象类同样适用。
function test<T extends MyInter>(arg: T): number{
return arg.length;
}
B站尚硅谷视频