指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
/*
* @Description:
* @Author: 若城
* @Date: 2024-01-17 15:05:26
* @LastEditTime: 2024-01-17 15:18:56
*/
// 泛型: 在定义函数/接口/类的时候不能预先确定要使用的数据的类型,而是在使用函数/接口/类的时候才能确定的数据的类型叫做泛型
(()=>{
// 需求: 定义一个函数, 传入两个参数, 第一个参数是数据, 第二个参数是数量,
// 函数的作用: 根据数量产生对应个数的数据, 存放在一个数组中
// 定义一个函数
function getArr<T>(value:T, count:number):T[]{
// 根据数据和数量产生一个数组
// const arr:T[] =[]
const arr:Array<T> = []
for (let i = 0; i < count; i++) {
arr.push(value)
}
return arr
}
const arr1 = getArr<number>(100.123,3)
const arr2 = getArr<string>('100.123',3)
console.log(
arr1, arr2
);
})()
函数中有多个泛型的参数
// 函数中有多个泛型的参数
(()=>{
function getMsg<K,T>(value1:K,value2:T):[K,T] {
return [value1, value2]
}
const arr1 = getMsg<string, number>('李白',1000)
console.log(arr1[0].split(''), arr1[1].toFixed(2));
})()
在定义接口时, 为接口中的属性或方法定义泛型类型在使用接口时, 再指定具体的泛型类型**
interface IbaseCRUD <T> {
data: T[]
add: (t: T) => void
getById: (id: number) => T
}
class User {
id?: number; //id主键自增
name: string; //姓名
age: number; //年龄
constructor (name, age) {
this.name = name
this.age = age
}
}
class UserCRUD implements IbaseCRUD <User> {
data: User[] = []
add(user: User): void {
user = {...user, id: Date.now()}
this.data.push(user)
console.log('保存user', user.id)
}
getById(id: number): User {
return this.data.find(item => item.id===id)
}
}
const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)
在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
class GenericNumber<T> {
zeroValue: T
add: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
return x + y
}
let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) {
return x + y
}
console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))
如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性
// 没有泛型约束
function fn <T>(x: T): void {
// console.log(x.length) // error
}
我们可以使用泛型约束来实现
interface Lengthwise {
length: number;
}
// 指定泛型约束
function fn2 <T extends Lengthwise>(x: T): void {
console.log(x.length)
}
我们需要传入符合约束类型的值,必须包含必须 length 属性:
fn2('abc')
// fn2(123) // error number没有length属性
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能 假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过 script 标签引入 jQuery,然后就可以使用全局变量 或 jQuery 了。 但是在 ts 中,编译器并不知道 或 jQuery 是什么东西 这时,我们需要使用 declare var 来定义它的类型
declare var jQuery: (selector: string) => any;
jQuery('#foo');
declare var 并没有真的定义一个变量,只是定义了全局变量 jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。它编译结果是:
jQuery('#foo');
一般声明文件都会单独写成一个 xxx.d.ts 文件 创建 01_jQuery.d.ts, 将声明语句定义其中, TS编译器会扫描并加载项目中所有的TS声明文件
declare var jQuery: (selector: string) => any;
很多的第三方库都定义了对应的声明文件库, 库文件名一般为 @types/xxx, 可以在 https://www.npmjs.com/package/package 进行搜索 有的第三库在下载时就会自动下载对应的声明文件库(比如: webpack),有的可能需要单独下载(比如jQuery/react)
JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。 内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。
Boolean Number String Date RegExp Error
/* 1. ECMAScript 的内置对象 */
let b: Boolean = new Boolean(1)
let n: Number = new Number(true)
let s: String = new String('abc')
let d: Date = new Date()
let r: RegExp = /^1/
let e: Error = new Error('error message')
b = true
// let bb: boolean = new Boolean(2) // error
Window Document HTMLElement DocumentFragment Event NodeList
const div: HTMLElement = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent) => {
console.dir(event.target)
})
const fragment: DocumentFragment = document.createDocumentFragment()