有关TS(TypeScript)的知识点记录,各位大佬不喜勿喷
1. 安装TS
npm i -g typescript
通过tsc -v检查是否安装成功
2. ts的文件中如果直接书写了js语法的代码,那么在html文件中直接引入ts文件,在谷歌浏览器中是可以直接使用的
如果ts文件中有了ts的语法代码,那么就需要把这个ts文件编译成js文件,在html文件中引入js文件来使用
tsc ts文件路径 + 回车可以将ts文件转换为js文件
ts文件中的函数中的形参,如果使用了某个类型进行修饰,最终在编译的js文件中是没有这个类型的
ts文件中的变量使用的是let进行修饰,编译的js文件中的修饰符就变成了var了
3. vscode自动编译
1). 生成配置文件tsconfig.json
tsc --init
2). 修改tsconfig.json配置
"outDir": "./js",
"strict": false,
3). 启动监视任务:
终端 -> 运行任务 -> 监视tsconfig.json
4. 类型注解
一种轻量级的为函数或者变量添加的约束
在变量后书写:类型
例如:function sayhai(str:string) {
return 'hello' + str
}
5. 接口interface,规范你的数据类型
(() => {
//定义一个接口
interface IPerson {
firstname:string//姓氏
lastname:string//名字
}
// 输出姓名
function showFullName(person:IPerson) {
return person.firstname + '_' + person.lastname
}
const person = {
firstname:'东方',
lastname:'明珠'
}
console.log(showFullName(person))
})()
6. 类的演示
(() => {
// 定义一个接口
interface IPerson {
firstname:string
lastname:string
}
// 定义一个类
class Person {
// 定义公共的字段(属性)
firstname:string
lastname:string
fullname:string //姓名
// 定义一个构造器函数
constructor (firstname:string,lastname:string){
// 更新属性数据
this.firstname = firstname
this.lastname = lastname
this.fullname = this.firstname + '_' + this.lastname
}
}
// 实例化对象,构建出来一个person传递到showFullName当中
const person = new Person('诸葛','孔明')
// 定义个函数
function showFullName(person:IPerson) {
return person.firstname + '_' + person.lastname
}
console.log(showFullName(person))
})()
7. 使用webpack打包TS
创建webpack文件夹,内部存放
public文件夹 => 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>Document</title>
</head>
<body>
</body>
</html>
src文件夹 => main.ts
document.write('s')
build文件夹 => webpack.config.js
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
const isProd = process.env.NODE_ENV === 'production' // 是否生产环境
function resolve (dir) {
return path.resolve(__dirname, '..', dir)
}
module.exports = {
mode: isProd ? 'production' : 'development',
entry: {
app: './src/main.ts'
},
output: {
path: resolve('dist'),
filename: '[name].[contenthash:8].js'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
include: [resolve('src')]
}
]
},
plugins: [
new CleanWebpackPlugin({
}),
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
devtool: isProd ? 'cheap-module-source-map' : 'cheap-module-eval-source-map',
devServer: {
host: 'localhost', // 主机名
stats: 'errors-only', // 打包日志输出输出错误信息
port: 8081,
open: true
},
}
终端打开webpack文件夹,执行命令
npm init -y
tsc --init
npm i -D typescript@4.0.5
npm i -D webpack@4.41.5 webpack-cli@3.3.10
npm i -D webpack-dev-server@3.10.2
npm i -D html-webpack-plugin@4.5.0 clean-webpack-plugin@3.0.0
npm i -D ts-loader@8.0.11
npm i -D cross-env@7.0.2
替换掉package.json文件scripts下的原有内容
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
npm run dev运行项目
npm run build打包
强制安装:
npm install --force
npm install --legacy -peer -deps
8. 基础类型
布尔值
最基本的数据类型就是简单的 true/false 值
let isDone: boolean = false;
isDone = true;
// isDone = 2 // error
数字
TypeScript 里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015中引入的二进制和八进制字面量。
let a1: number = 10 // 十进制
let a2: number = 0b1010 // 二进制
let a3: number = 0o12 // 八进制
let a4: number = 0xa // 十六进制
字符串
JavaScript 程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用 string 表示文本数据类型。 和 JavaScript 一样,可以使用双引号(")或单引号(')表示字符串
let name1:string = 'tom'
name1 = 'jack'
// name = 12 // error
let age:number = 12
const info = `My name is ${name}, I am ${age} years old!`
undefine和null
TypeScript 里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null。
默认情况下 null 和 undefined 是所有类型的子类型。 就是说你可以把 null 和 undefined 赋值给 number 类型的变量。
let u: undefined = undefined
let n: null = null
数组
TypeScript 像 JavaScript 一样可以操作数组元素。
元素类型后面接上[],表示由此类型元素组成的一个数组:
let list1: number[] = [1, 2, 3]
第二种方式是使用数组泛型,Array<元素类型>:
let list2: Array<number> = [1, 2, 3]
元组 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string 和 number 类型的元组。
注意:数据的类型和个数应该和在定义元组的时候的数据类型及位置是一致的
let t1: [string, number]
t1 = ['hello', 10] // OK
t1 = [10, 'hello'] // Error
当访问一个已知索引的元素,会得到正确的类型:
console.log(t1[0].substring(1)) // OK
console.log(t1[1].substring(1)) // Error, 'number' 不存在 'substring' 方法
枚举
enum 类型是对 JavaScript 标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字
枚举里面的每个数据值都可以叫元素,每个元素都有自己的编号,编号是从0开始的,依次递增加1
enum Color {
Red,
Green,
Blue
}
枚举数值默认从0开始依次递增
根据特定的名称得到对应的枚举数值
let myColor: Color = Color.Green // 1
console.log(myColor, Color.Red, Color.Blue) // 1 0 2
默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1 开始编号:
enum Color1 {Red = 10, Green, Blue}
let c: Color1 = Color1.Green
console.log(Color1.Red,c,Color1.Blue) // 10 11 12
或者,全部都采用手动赋值:
enum Color2 {Red = 1, Green = 2, Blue = 4}
let d: Color2 = Color2.Green
console.log(Color2.Red , d, Color2.Blue) // 1 2 4
枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:
enum Color3 {Red = 4, Green, Blue}
let colorName: string = Color3[5]
console.log(colorName) // 'Green'
any
为不清楚类型的变量指定一个类型
let notSure: any = 4
notSure = 'maybe a string'
notSure = false // 也可以是个 boolean
let list: any[] = [1, true, 'free']
list[1] = 100
void
void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void
表示没有任何类型, 一般用来说明函数的返回值不能是undefined和null之外的值
function fn(): void {
console.log('fn()')
// return undefined
// return null
// return 1 // error
}
声明一个 void 类型的变量没有什么大用,因为你只能为它赋予 undefined 和 null:
let unusable: void = undefined
object
object 表示非原始类型,也就是除 number,string,boolean之外的类型
function fn2(obj:object):object {
console.log('fn2()', obj)
return {}
// return undefined
// return null
}
console.log(fn2(new String('abc')))
// console.log(fn2('abc') // error
console.log(fn2(String))
联合类型
联合类型(Union Types)表示取值可以为多种类型中的一种
需求1: 定义一个一个函数得到一个数字或字符串值的字符串形式值
function toString2(x: number | string) : string {
return x.toString()
}
需求2: 定义一个一个函数得到一个数字或字符串值的长度
function getLength(y: string | number) : number {
// return x.length // error
let str = 'abc'
console.log(str.length)
if (typeof y === 'string') { // error
return y.length
} else {
return y.toString().length
}
}
9. 类型断言
类型断言有两种形式。 其一是“尖括号”语法, 另一个为 as 语法
类型断言(Type Assertion): 可以用来手动指定一个值的类型
语法:
方式一: <类型>值
方式二: 值 as 类型 tsx中只能用这种方式
需求: 定义一个函数得到一个字符串或者数值数据的长度 */
function getLength(x: number | string) {
if ((<string>x).length) {
return (x as string).length
} else {
return x.toString().length
}
}
console.log(getLength('abcd'), getLength(1234))
10. 类型推断
TS会在没有明确的指定类型的时候推测出一个类型
有下面2种情况: 1. 定义变量时赋值了, 推断为对应的类型. 2. 定义变量时没有赋值, 推断为any类型
定义变量时赋值了, 推断为对应的类型
let b9 = 123 // number
// b9 = 'abc' // error
b9 = 20
定义变量时没有赋值, 推断为any类型
let b10 // any类型
b10 = 123
b10 = 'abc'
11. 接口
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口是对象的状态(属性)和行为(方法)的抽象(描述)
定义人的接口
(() => {
// 定义一个接口类型,该接口作为person对象的类型使用,限定或者约束该对象中的属性数据
interface IPerson{
id:number,
name:string,
age:number,
sex:string
}
// 定义一个对象,该对象的类型就是我定义的接口
const Person:IPerson = {
id:1,
name:"张三",
age:18,
sex:"男"
}
})()
12. 可选属性
接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。
interface IPerson {
id: number
name: string
age: number
sex?: string
}
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号。
可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
const person2: IPerson = {
id: 1,
name: 'tom',
age: 20,
// sex: '男' // 可以没有
}
13. 只读属性readonly
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性:
interface IPerson {
readonly id: number
name: string
age: number
sex?: string
}
一旦赋值后再也不能被改变了。
const person2: IPerson = {
id: 2,
name: 'tom',
age: 20,
// sex: '男' // 可以没有
// xxx: 12 // error 没有在接口中定义, 不能有
}
person2.id = 2 // error
readonly vs const
最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用 readonly。
14. 函数类型
接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
接口可以描述函数类型(参数的类型与返回的类型)
interface SearchFunc {
(source: string, subString: string): boolean
}
这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。
const mySearch: SearchFunc = function (source: string, sub: string): boolean {
return source.search(sub) > -1
}
console.log(mySearch('abcd', 'bc'))
(() => {
// 函数类型:通过接口的方式作为函数的类型来使用
示例:
// 定义一个接口,用来作为某个函数的类型使用
interface IsSearchFunc{
// 定义一个调用签名
(source:string,subString:string): boolean
}
// 定义一个函数,该类型就是上面定义的接口
const searchString: IsSearchFunc = function(source:string,subString:string): boolean {
return source.search(subString) > -1
}
// 调用函数
console.log(searchString('哈哈,我是老六','六'))
})()
15. 类类型
类的类型可以通过接口来实现
类可以通过接口的方式,来定义当前这个类的类型
类可以实现一个接口,类也可以实现多个接口,要注意,接口中的内容都要真正实现
接口可以继承其他的多个接口
接口和接口之间叫继承(使用的是extends关键字),类和接口之间叫实现(使用的是implements)
示例
(() => {
// 定义一个接口
interface IFly{
// 该方法没有任何的实现(方法中什么都没有)
fly()
}
// 定义一个类,这个类的类型就是上面接口的实现(实际上也可以理解为,IFly接口约束了当前的Person类)
class Person implements IFly{
// 实现接口中的方法
fly() {
console.log('飞')
}
}
// 实例化对象
const person = new Person()
person.fly()
})()
16. 类
类:可以理解为一个模板,通过模板可以实例化对象
面向对象的编程思想
(() => {
// ts中类的定义以及使用
class Person {
// 定义属性
name:string
age:number
gender:string
// 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
constructor(name:string='小甜甜',age:number=16,gender:string='女') {
// 更新对象中的属性数据
this.name = name
this.age = age
this.gender = gender
}
// 定义实例方法
sayhai(str:string) {
console.log(`大家好,我是${this.name},今年已经${this.age}岁了,是个${this.gender}孩子`,str)
}
}
// ts中使用类,实例化对象,可以直接进行初始化操作
const person = new Person('佐助',18,'男')
person.sayhai('你叫什么名字啊')
})()
17. 继承
继承:类与类之间的关系
继承后类与类之间的叫法,A类继承B类,此时A类叫子类,B类叫基类
子类 ===> 派生类,基类 ===> 超类,父类
一旦发生了继承的关系,就出现了父子类的关系
类和类之间如果要有继承关系,要使用extends关键字
子类中可以调用父类中的构造函数,使用的是super关键字(包括调用父类中的实例方法,也可以使用super)
子类中可以重写父类的方法
(() => {
// 定义一个类,作为父类
class Person{
// 定义属性
name:string
age:number
gender:string
// 定义构造函数
constructor(name:string='小明',age:number=18,gender:string='男') {
// 更新属性数据
this.name = name
this.age = age
this.gender = gender
}
// 定义实例方法
sayhai(str:string) {
console.log(`我是${this.name},${str}`)
}
}
// 定义一个类,继承Person
class Student extends Person{
constructor(name:string,age:number,gender:string) {
// 调用父类中的构造函数,使用的是super
super(name,age,gender)
}
// 可以调用父类中的方法
sayhai() {
console.log('我是学生类中的sayhai')
// 调用父类中的sayhai方法
super.sayhai('哈哈')
}
}
// 实例化Person
const person = new Person()
person.sayhai('老六')
// 实例化Student
const student = new Student('小明',16,'女孩子')
student.sayhai()
})()
18. 多态
多态:父类型的引用指向子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为
(() => {
// 定义一个父类
class Animal {
// 定义一个属性
name:string
// 定义一个构造函数
constructor(name:string) {
this.name = name//更新属性值
}
// 实例方法
run(distance:number=0) {
console.log(`跑了${distance}米`)
}
}
// 定义一个子类
class Dog extends Animal{
constructor(name:string) {
super(name)//调用父类的构造函数,实现子类中属性的初始化操作
}
// 实例方法,重写父类的实例方法
run(distance:number=5) {
console.log(`跑了${distance}米`)
}
}
// 再定义一个子类
class Pig extends Animal{
constructor(name:string) {
super(name)//调用父类的构造函数,实现子类中属性的初始化操作
}
// 实例方法,重写父类的实例方法
run(distance:number=10) {
console.log(`跑了${distance}米`)
}
}
// 实例化父类对象
const ani: Animal = new Animal('动物')
ani.run()
// 实例化子类对象
const dog: Dog = new Dog('大黄')
dog.run()
const pig: Pig = new Pig('八戒')
pig.run()
console.log('===========')
// 父类和子类的关系:父类的类型创建子类的对象
const dog1: Animal = new Dog('小黄')
dog1.run()
const pig1: Animal = new Pig('小猪')
pig1.run()
console.log('===========')
// 该函数需要的参数是Animal类型的
function showRun(ani:Animal) {
ani.run()
}
showRun(dog1)
showRun(pig1)
})()
19. 公共,私有与受保护的修饰符
修饰符(类中额成员的修饰符):主要是描述类中的成员(属性,构造函数,方法)的可访问性
访问修饰符: 用来描述类内部的属性/方法的可访问性
public: 默认值, 公开的外部也可以访问
private: 只能类内部可以访问
protected: 类内部和子类可以访问
默认为 public
在 TypeScript 里,成员都默认为 public。
你也可以明确的将一个成员标记成 public。
理解 private
当成员被标记成 private 时,它就不能在声明它的类的外部访问。
理解 protected
protected 修饰符与 private 修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。
20. readonly
首先是一个关键字,对类中的属性成员进行修饰,修饰后,该属性成员就不能在外部被随意的修改了
可以在实例化对象的时候进行修改
如果构造函数中,没有任何的参数,类中的属性成员,此时已经使用readonly进行修饰了,那么外部也是不能对这个属性进行更改的
类中的普通方法中,也不能修改readonly修饰的成员属性值
构造函数constructor中,可以对只读成员进行修改
如果构造函数中没有任何的参数,类中的属性成员此时已经用readonly修饰了,那么外部也是不能对这个属性修改的
构造函数中的name参数一旦使用readonly进行修饰,那么该name参数可以叫做参数属性
构造函数中的name参数一旦使用readonly进行修饰,那么该类中就有了一个name的属性成员了,构造函数中的this.name = name就不用写了
构造函数中的name参数一旦使用readonly进行修饰,外部也是无法修改类中的name属性成员值的
构造函数中的name参数一旦使用public/privte/protected进行修饰,那么该类中就有了一个name的属性成员了,构造函数中的this.name = name就不用写了
21. 存取器
TypeScript 支持通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
存取器:让我们可以有效的控制对象中的成员的访问,通过getters和setters来进行操作
(() => {
// 使用get和set控制数据
class Person {
firstName:string
lastName:string
constructor(firstName:string,lastName:string) {
this.firstName = firstName
this.lastName = lastName
}
// 读取器,负责读取数据
get fullName() {
console.log('执行get')
return this.firstName + '_' + this.lastName
}
// 设置器,负责设置数据
set fullName(val) {
console.log('执行set')
this.firstName = val.split('_')[0]
this.lastName = val.split('_')[1]
}
}
// 实例化对象
const person = new Person('东方','不败')
console.log(person)
console.log(person.fullName)
person.fullName = '诸葛_孔明'
console.log(person)
console.log(person.fullName)
})()
22. 静态成员
在类中通过static修饰的属性或者方法,就是静态属性/静态方法,也称之为静态成员
静态成员在使用的时候,是通过类名.的这种语法来调用的
构造函数是不能通过构造函数来进行修饰的
(() => {
// 定义一个类
class Person {
// 类中默认有一个内置的name属性,所以会报错
// static name:string
static name1:string = '张三'
// constructor(name:string) {
constructor() {
// 此时this是实例对象,name1是静态属性,不能通过实例对象直接调用静态属性来使用
// this.name1 = name
}
static sayhai() {
console.log('老六')
}
}
// const person = new Person()
// const person = new Person('张三')
// console.log(person.name1)
// person.sayhai()
// 通过类名.静态属性的方式来访问该成员数据
console.log(Person.name1)
// 通过类名.静态属性的方式来设置该成员数据
Person.name1 = '李四'
console.log(Person.name1)
// 通过类名.静态方法的方式调用静态方法
Person.sayhai()
})()
23. 抽象类
抽象类做为其它派生类的基类使用。 它们不能被实例化。不同于接口,抽象类可以包含成员的实现细节。 abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法。
抽象类所包含的抽象方法一般没有任何的具体内容的实现
(() => {
// 定义一个抽象类
abstract class Animal{
// 抽象属性
abstract name1:string
// 抽象方法
abstract eat()
// 实例方法
sayhai() {
console.log('你好')
}
}
// 定义一个子类
class Dog extends Animal{
name1: string = '小黄'
eat() {
console.log('舔着吃',this.name1)
}
}
// 不能实例化抽象类
// const ani:Animal = new Animal()
// 重新的实现抽象类中的方法,此时的方法就是当前Dog类的实例方法了
const dog:Dog = new Dog()
console.log(dog)
console.log(dog.name1)
dog.eat()
// 调用的是抽象类中的实例方法
dog.sayhai()
})()
24. 函数和函数类型
// 函数:封装了一些复用性代码,在需要的时候直接调用即可
(() => {
// js书写方式,ts中也可以这样写
// // 函数声明,命名函数
// function add(x , y) {
// return x + y
// }
// // 函数表达式,匿名函数
// const add2 = function(x , y) {
// return x + y
// }
// ts书写方式
// 函数声明,命名函数
function add(x:string , y:string): string {
return x + y
}
// 函数表达式,匿名函数
const add2 = function(x:number , y:number): number {
return x + y
}
console.log(add('ab' , 'cd'))
console.log(add2(10 , 20))
// 完整写法
// myAdd是变量名
// x: number, y: number当前函数类型
// function(x: number, y: number): number就相当于符合上面的这个函数类型的值
let myAdd: (x: number, y: number) => number = function(x: number, y: number): number {
return x + y
}
console.log(myAdd(10,20))
})()
25. 可选参数和默认参数
可选参数:函数在声明的时候,内部的参数使用了?进行修饰,那么就表示该参数可以传入也可以不传入
默认参数:函数在声明的时候,内部的参数有自己的默认值,此时这个参数就可以叫默认参数
(() => {
// 定义一个函数:传入姓氏和名字可以得到姓名
// 需求:如果不传入任何内容,就返回默认的姓氏
// 需求:如果只传入姓氏,那么就返回姓氏
// 需求:如果只传入姓氏和名字,那么就返回姓名
const getFullName = function(firstName:string = '诸葛',lastName?:string): string {
if(lastName) {
return firstName + '_' + lastName
}else {
return firstName
}
}
// 函数调用
console.log(getFullName())
console.log(getFullName('东方'))
console.log(getFullName('东方','不败'))
})()
26. 剩余参数
剩余参数(rest参数):必须放在函数声明的时候所有参数的最后
(() => {
// ...args:string[]表示剩余参数放在了一个字符串的数组中,agrs里面
function showMsg(str:string,...args:string[]) {
console.log(str) // a
console.log(args) // ['b', 'c', 'd']
}
showMsg('a','b','c','d')
})()
27. 函数重载
函数重载:函数名字相同,函数的参数及个数不同
函数重载: 函数名相同, 而形参不同的多个函数
需求: 我们有一个add函数,它可以接收2个string类型的参数进行拼接,也可以接收2个number类型的参数进行相加
(() => {
// 函数重载声明
function add(x:string, y:string):string
function add(x:number, y:number):number
// 函数声明
function add(x:string | number,y:string | number): string | number {
if(typeof x === 'string' && typeof y === 'string') {
return x + y
}else if(typeof x === 'number' && typeof y === 'number') {
return x + y
}
}
console.log(add('诸葛', '孔明'))
console.log(add(10, 20))
// 此时ts应该给我传递出错误的内容
// console.log(add('诸葛', 20))
// console.log(add(10, '孔明'))
})()
28. 泛型
泛型:在定义函数,接口,类的时候不能预先确定要使用的数据的类型,而是在使用函数,接口,类的时候才能确定数据的类型
(() => {
// 需求:定义一个函数,传入两个参数,第一个参数是数据,第二个参数是数量
// 根据数量产生对应个数的数据,存放在数组中
// 定义一个函数
function getArr(val:number,count:number): number[] {
const arr: number[] = []
for(let i = 0 ; i < count ; i++) {
arr.push(val)
}
return arr
}
// 调用函数
const arr1 = getArr(100.123,3)
console.log(arr1)
// const arr2 = getArr('abc',3)报错
// 定义一个函数,只不过传入的是字符串类型
function getArr2(val:string,count:number): string[] {
const arr: string[] = []
for(let i = 0 ; i < count ; i++) {
arr.push(val)
}
return arr
}
// 调用函数
const arr2 = getArr2('abc',3)
console.log(arr2)
// const arr3 = getArr2(123,3)报错
// 定义一个函数,可以传入任意类型的数据
function getArr3(val:any,count:number): any[] {
const arr: any[] = []
for(let i = 0 ; i < count ; i++) {
arr.push(val)
}
return arr
}
// 调用函数
const arr3 = getArr3('abc',3)
console.log(arr3)
const arr4 = getArr3(123,3)
console.log(arr4)
// arr3存储的是字符串类型的数据
// arr4存储的是数字类型的数据
console.log(arr3[0].split(''))//没有任何提示信息
console.log(arr4[0].toFixed(2))//没有任何提示信息
// 定义一个函数,可以传入任意类型的数据
function getArr4<T>(val:T,count:number): any[] {
const arr: T[] = []
for(let i = 0 ; i < count ; i++) {
arr.push(val)
}
return arr
}
// 调用函数
const arr5 = getArr4<string>('abc',3)
const arr6 = getArr4<number>(123,3)
console.log(arr5)
console.log(arr6)
console.log(arr3[0].split(''))//有提示
console.log(arr3[0].toFixed(2))//有提升
})()
29. 多个泛型参数的函数
多个泛型参数的函数:函数中有多个泛型的参数
(() => {
function getMsg<K, V>(value1:K, value2:V): [K, V] {
return [value1, value2]
}
const arr1 = getMsg<string, number>('jack', 100.23)
console.log(arr1[0].split(''))
console.log(arr1[1].toFixed(2))
})()
30. 泛型接口
泛型接口:在定义接口时,为接口中的属性或者方法定义泛型类型,在使用接口时,在指定具体的泛型类型
(() => {
// 需求:定义一个类,用来存储用户的相关信息(id,name,age)
// 通过一个类的实例对象,调用add方法,可以添加多个用户信息对象
// 调用getUserId可以根据id获取某个指定的用户信息对象
// 定义一个泛型接口
interface IbaseCRUD<T>{
data:Array<T>
add:(t:T) => T
getUserId:(id:number) => T
}
// 定义一个用户信息的类
class User{
id?:number
name:string
age:number
constructor(name:string,age:number) {
this.name = name
this.age = age
}
}
// 定义一个类,可以针对用户的信息对象进行增加及查询的操作
// CRUD:create,read,update,delete
class UserCRUD implements IbaseCRUD<User>{
data:Array<User> = []// 存储信息
// 用于存储用户信息对象的
add(user:User): User{
user.id = Date.now() + Math.random()
this.data.push(user)
return user
}
// 方法根据id查询指定的用户信息对象
getUserId(id:number): User{
return this.data.find(user => user.id === id)
}
}
// 实例化添加用户信息对象的类
const userCRUD: UserCRUD = new UserCRUD()
userCRUD.add(new User('张三',20))
userCRUD.add(new User('李四',25))
userCRUD.add(new User('王五',27))
console.log(userCRUD.data)
const {id} = userCRUD.add(new User('赵六',23))
console.log(userCRUD.getUserId(id))
})()
31. 泛型类
在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
(() => {
// 定义一个类,类中的属性值的类型不确定,方法中的参数及返回值的类型也不确定
// 定义一个泛型类
class GenericNumber<T>{
// 默认的属性的值的类型是泛型类型
defaultValue:T
add:(x:T, y:T) => T
}
// 实例化类的对象的时候,再确定泛型的类型
const g1:GenericNumber<number> = new GenericNumber<number>()
g1.defaultValue = 25
// g1.defaultValue = '123'
// 相加的方法
g1.add = function(x, y) {
return x + y
}
console.log(g1.add(10, 20))
// 实例化类的对象的时候,再确定泛型的类型
const g2:GenericNumber<string> = new GenericNumber<string>()
// g2.defaultValue = 25
g2.defaultValue = '123'
// 相加的方法
g2.add = function(x, y) {
return x + y
}
console.log(g2.add('ab', 'cd'))
})()
32. 泛型约束
如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性
(() => {
// 定义一个接口用于约束将来某个类型必须有length属性
interface ILength{
length:number
}
function getLength<T extends ILength>(x: T):number {
return x.length
}
console.log(getLength<string>('what are you doing'))
// console.log(getLength<number>(18))
})()