师从尚硅谷教学~
ts官网学习:TypeScript: JavaScript With Syntax For Types.
中文官网:文档简介 · TypeScript中文网 · TypeScript——JavaScript的超集
菜鸟教程:TypeScript 教程 | 菜鸟教程
尚硅谷视频: 尚硅谷TypeScript教程(李立超老师TS新课)_哔哩哔哩_bilibili
掘金TS史上最强学习入门文章:2022 typescript史上最强学习入门文章(2w字) - 掘金
推荐学习网址:TypeScript 入门教程
一、TS 初识
1.1、ts介绍
1、微软开发的一款开源的编程语言
2、是JavaScript的超集,遵循最新的ES6、ES5规范。typescript扩展了JavaScript的语法
3、本质上添加了可选的静态类型和基于类的面向对象编程,通俗的理解为JavaScript的一个特殊版本,其语法规范严谨,适用于开发大型项目应用
4、谷歌也在大力支持typescript的推广,谷歌的angular2.x就是基于typescript
5、最新的vue、react也集成typescript
6、nodeJS框架的Nextjs、midway中用的就是typescript
1.2、ts 开发环境搭建及自动编译
1、全局安装,
npm i typescript -g
2、运行ts,之后会把ts文件编译为js文件
tsc demo.ts
3、自动编译配置及运行
① tsc --init,创建tsconfig.json文件
② 在tsconfig.json中,加入 "outDir": "./js",
③ tsc -p tsconfig.json --watch;自动编译生效;
二、TS 中的类型
*** 图解
1.1、 any 类型
1. any 表示的是任意类型,一个变量设置类型为 any 后相当于对该变量 关闭了TS的类型检测;
***使用TS 的时,不建议使用any类型,
例:let d:ang;❌不建议;
2. 声明变量如果不指定类型,则TS 解析器会自动判断变量的类型为 any,(隐式的any)
let d;
d = 10; //d 的隐式类型为any
1.2、 unknow
表示未知类型的值
1. unknow 实际上就是一个类型安全的any类型;
2. unknow 类型的变量,不能直接赋值给其他变量;
3. any类型会影响其他变量类型的规定,未知变量类型是使用unknow;
例:
let e: unknow;
e = 10;
e = 'hello';
e = true;
4. 如果需要变量赋值,可以使用 if 来判断是否是此类型,进而赋值
if(typeof e === 'string'){
s = e
}
5. 类型断言,可以用来告诉解析器变量的实际类型
/* 语法:
变量 as 类型;
<类型>变量
*/
s = e as string;
s = <string>e;
1.3、 函数类型的定义、函数结构类型声明;
1. void 用来表示空,以函数为例,就是表示没有返回值的函数
function fn(): void{
}
2. never 表示永远不会返回结果
function fn2(): never{
throw new Error('报错了');
}
3. 设置函数结构的类型声明:
语法:(形参:类型,形参:类型...) => 返回值
let d:(a: number,b: number) => number; //指定义一个类型,参数类型都为number,函数返回值也是number;
例:相当于;
d = function (n1: string,n2: string): number{
return 10;
}
1.4、 对象 object 的类型定义
1. // object 表示一个js对象,❌,一般不使用object来约束类型,js中一切皆对象,无法约束;
let a: object;
a = {};
a = function(){ }
2. 约束对象类型的正确做法
// {} 用来指定对象中可以包含哪些属性;
// 语法: {属性1:属性值1,属性2:属性值2,...}
// 在属性名后边加上?,表示属性是可选择的;
例:
let b: {name: string,age?: number}; //表示约束变量b 的类型为对应name、age且age字段是可选择的;
b = {name: '孙悟空',age:18 };
3. 约束一个变量,固定一个字段属性,其他属性可选;
// [propName: string]: any 表示任意类型的属性;
let c: {name: string, [propName: string]: any};
c = {name:'唐僧',age: 18,gender: '男'}
1.5、数组的类型定义
1. 语法定义
类型[],
Array<类型>
// string[] 表示字符串数组
let e: string[];
e = ['a','b','c'];
// number[] 表示数值数组;
let f: number[];
或
let g: Array<number>;
g = [1,2,3];
1.6、 元组类型的定义
1. 元组,元组就是固定长度的数组
语法:[类型,类型,类型...]
2. 例:
let h: [string,number];
h = ['hello',123];
1.7、enum
枚举类型定义
// 定义枚举类型
enum Gender{
Male,
Female
}
let i: {name: string,gender: Gender};
i = {
name: '孙悟空',
gender:Gender.Male // 'male'
}
console.log(i.gender === Gender.Male); //true
1.8、类型的别名
type myType = 1 | 2 | 3 | 4 | 5; // | 或的意思
let k: myType;
let l: myType;
let m: myType;
// 此时,k、l、m可以且必须是1-5的值;
二、TS 中编译选项
2.1、 tsconfig.json
文件
1. tsconfig.json 是ts编译的配置文件,ts编译器可以根据配置信息对代码进行编译;
'include' 用来指定哪些ts文件需要被编译
路径: ** 表示任意目录
* 表示任意文件
'exclude' 不需要被编译的文件目录
默认值:['node_modules','bower_compoents','json_packages']
2. "include":[
"./src/**/*", //表示src文件夹下的所有文件都需要编译
]
3. extends,
定义被继承的配置文件
示例: 'extends':'./configs/base';
* 上述示例中表示,当前配置文件中会自动包含config目录下base.json文件中的所有配置信息;
4. files
* 指定被编译的文件列表,只有需要编译的文件少的时候才会使用到;
示例:
"files":[
"core.ts",
"sys.ts",
...
] //此时列表中的ts文件将会被编译
2.2、配置中的 compilerOptions
** 编译选项是配置文件中非常重要也是比较复杂的配置选项;
在compilerOptions中包含多个子选项,用来完成对编译的配置;
1. target 用来指定ts 被编译为 ES的版本
"target":"es2016", //可选项,es3,es5,es6,es2015.....
2. module 指定要使用的模块化规范
"module": "es2015", //可选值 'none','commonJs','amd','es6',....
3. lib用来指定项目中使用的库
"lib": ["es6","dom"]
可选值,.....
4. outDir 用来指定编译后文件所在的目录
"outDir": "./dist",
5. outFile,将代码合并为一个文件
设置outFile后,所有全局作用域中的代码会合并到同一个文件中,
"outFile": "./dist/app.js",
**一般不使用合并的选项,会后续ts结合打包工具webpack来实现编译打包;
2.3、 compilerOptions
中的严格检查
1. 是否编译js文件
"allowJs": true,
2. 检查js代码是否符合语法规范,默认是false
"checkJs":true,
3. 是否移除注释
"removeComments":true,
4. 只执行编译,不生成编译后的文件
"noEmit":false,
5. 当有错误时,不生成编译后的文件
"noEmitOnError":true
***********是否开启严格模式***************
1. 开启严格模式的总开关,开启后其他项均为true;如果需要单独关闭,单独指定false
"strict":true,
2. 用来设置编译后的文件是否使用严格模式,默认false
"alwaysStrict":true
3. 不允许出现隐式的 any 类型
"noImplicitAny":true
4. 不允许不明确类型的 this
"noImplicitThis":true
5. 严格的检查空值
"strictNullChecks":true
***************示例:解决出现空值后的报错***************
let box1 = document.getElementById('box1');
// box1 的值可能出现null的情况;
//解决
① 条件判断解决
if(box1 !== null){
box1.addEventListener('click',function(){
alert('hello ts')
})
}
② 问号方法
box1?.addEventListener('click',function(){
alert('hello ts')
})
三、使用webpack
结合ts打包
3.1、 基本使用步骤
1. npm init 项目初始化生成package.json文件;
2. 下载安装对应的包文件
cnpm i -D webpack webpack-cli typescript ts-loader
3. 项目根目录创建webpack.config.js配置文件
// 引入一个包
const path = require('path');
// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
// 指定入口文件
entry: "./src/index.ts",
// 指定打包文件所在的目录
output: {
path: path.resolve(__dirname,'dist'),
// 打包后文件的位置
filename: 'bundle.js'
},
// 指定webpack 打包时,使用的模块
module: {
//指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
// 要使用的loader加载器
use: 'ts-loader',
// 要排除的文件
exclude: /node-modules/
}
]
}
}
4. 根目录创建tsconfig.json配置文件
{
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true
}
}
5. 在package.json中配置webpack打包选项build;
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
6. 此时执行
npm run build 就可以执行webpack打包了
3.2、 配置webpack
的插件
1. 安装html插件,
cnpm i html-webpack-plugin
2. // 引入html插件
const HTMLWebpackPlugin = require('html-webpack-plugin');
3. 配置插件,自动生成index.html文件
// 配置webpack插件,可以在dist文件下自动生成index.html文件
module.exports = {
plugins: [
new HtmlWebpackPlugin(),
]
}
4. 加入配置文件 修改生成的html文件的标题以及其他
plugins: [
new HtmlWebpackPlugin({
title: "这是一个自定义title"
}),
]
5. 使用index.html模板文件
在src文件下定义一个模板html文件;
new HtmlWebpackPlugin({
// title: "这是一个自定义title"
template: './src/index.html'
}),
6. 配置dev-server 自动编译加载浏览器
cnpm i -D webpack-dev-server
*在package.json文件下scripts下定义"start"
7. 引入clean插件,重新构建前先删除dist文件,再次构建
cnpm i clean-webpack-plugin
// 引入clean插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
// title: "这是一个自定义title"
template: './src/index.html'
}),
]
8. webpack用来设置引用模块。不配置打包会报错
// 用来设置引用模板
resolve:{
extensions: ['.ts','.js']
}
3.3、配置Bebal
插件,来实现ts的兼容性
1. 安装 插件包
cnpm i -D @babel/core @babel/preset-env babel-loader core-js
2. 添加配置项
use: [
// 配置babel,指定加载器
{
loader:"babel-loader",
//设置babel
options: {
// 设置预定义的环境
presets:[
[
//指定环境插件
"@babel/preset-env",
// 配置信息
{
// 要兼容的目标浏览器
targets:{
"chrome":"58",
"ie":"11"
},
// 指定corejs的版本
// 兼容一些高级语法,如promise在ie中没有
"corejs":"3",
// 使用corejs的方式 "usage" 表示按需加载
"useBuiltIns":"usage"
}
]
]
}
},
'ts-loader',
],
3. 默认webpack 的语法为箭头函数打包,支持ie,需要修改配置项
output: {
// 告诉webpack不使用箭头函数,来实现ie浏览器的兼容
environment:{
arrowFunction: false,
const: false
}
},
3.5、完整webpack.json
配置文件,TS部分
// 引入一个包
const path = require('path');
// 引入html插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入clean插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// webpack中的所有配置信息都应该写在module.exports中
module.exports = {
// 指定开发还是生产模式
mode: 'development',
// 指定入口文件
entry: "./src/index.ts",
// 指定打包文件所在的目录
output: {
path: path.resolve(__dirname,'dist'),
// 打包后文件的位置
filename: 'bundle.js',
// 告诉webpack不使用箭头函数,来实现ie浏览器的兼容
environment:{
arrowFunction: false
}
},
// 指定webpack 打包时,使用的模块
module: {
//指定要加载的规则
rules: [
{
// test指定的是规则生效的文件
test: /\.ts$/,
// 要使用的loader加载器
use: [
// 配置babel,指定加载器
{
loader:"babel-loader",
//设置babel
options: {
// 设置预定义的环境
presets:[
[
//指定环境插件
"@babel/preset-env",
// 配置信息
{
// 要兼容的目标浏览器
targets:{
"chrome":"58",
"ie":"11"
},
// 指定corejs的版本
// 兼容一些高级语法,如promise在ie中没有
"corejs":"3",
// 使用corejs的方式 "usage" 表示按需加载
"useBuiltIns":"usage"
}
]
]
}
},
'ts-loader',
],
// 要排除的文件
exclude: /node-modules/
}
]
},
// 配置webpack插件,可以在dist文件下自动生成index.html文件
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
// title: "这是一个自定义title"
template: './src/index.html'
}),
],
// 用来设置引用模板
resolve:{
extensions: ['.ts','.js']
}
}
四、TS中的 面向对象
4.1、面向对象简介
4.2、class 类的定义
-
定义类,语法
class 类名 { 属性: 类型; constructor(参数: 类型){ this.属性 = 参数; } 方法名(){ ... } }
-
概念:
// 使用class关键字来定义一个类
/**
* 对象中主要包含了两个部分内容
* 1.属性, 分为实例属性和静态属性
* ① 直接定义的属性是实例属性,需要通过对象的实例去访问
* const per = new Person;
* per.name; 来访问;
* ② 使用static开头的属性是静态属性&类属性
* Person.age直接访问
* 2.方法
* ① 实例方法
* ② 静态方法
*/
-
示例:
class Person {
// 定义属性,为实例属性,需要new出来之后访问
// readonly 对属性的只读属性,无法修改
// readonly name: string = '孙悟空';
name = '孙悟空' //可以直接定义,默认加入string类型
// 在属性前使用static关键字可以定义类属性(静态属性),static readonly 静态+只读
// static readonly age: number = 18;
static age = 18;
// 定义方法,实例方法;前面+static是静态方法
sayHello(){
console.log('hello 大家好');
}
static sayHello(){
console.log('hello 大家好,static');
}
}
const per = new Person;
console.log(per.name);
// per.name = 'jack';
console.log(per.name);
// 调用实例方法
console.log(per.sayHello());
console.log(per);
// console.log(per.age);
// Person.age = 16; //无法分配到 "age" ,因为它是只读属性。
console.log('Person.age',Person.age);
// 调用静态方法
console.log(Person.sayHello());
4.3、class 类中的构造函数和this
-
constructor 构造函数会在对象创建时调用
-
调用new函数就相当于调用构造函数constructor
-
在实例方法中,this就表示当前的实例
-
可以通过this向新建的对象中添加属性;
class Dog {
// name: string;
// age: number;
// constructor 被称为构造函数
// 构造函数会在对象创建时调用
constructor(name: string,age: number) { //构造函数传参
// console.log('构造函数执行了'); //调用new函数就相当于调用构造函数constructor
// 在实例方法中,this就表示当前的实例
// 在构造函数中当前对象就是当前新建的那个对象
// 所以,可以通过this向新建的对象中添加属性;
this.name = name;
this.age = age;
// console.log('this',this); // Dog {}
}
bark(){
alert('汪汪汪')
}
}
const dog1 = new Dog('小黑',3); //调用new函数就相当于调用构造函数constructor
const dog2 = new Dog('小白',5);
console.log(dog1);
console.log(dog2);
4.4、 类的继承
-
使用extends关键字,Dog子类继承父类Animal;
-
继承之后,子类将会拥有父类中所有的属性和方法;
-
通过继承可以将多个类中共有的代码写在一个父类中;
-
这样只需要写一次即可让所有的子类都同时拥有父类的属性;
-
如果需要在子类中添加一些父类中没有的属性或者方法,可以直接加属性和方法就可 *如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法 *这种子类覆盖父类方法的形式,我们称为方法的重写;
// 立即执行函数,让变量拥有作用域就可以重复命名了
(() => {
// 定义一个Animal动物公共类,将dog和cat类中的内容定义在动物类中
class Animal{
name: string;
age: number;
constructor(name: string,age: number){
this.name = name;
this.age = age;
}
sayHello(){
console.log('动物在叫....');
}
}
/*
- 使用extends关键字,Dog子类继承父类Animal;
*/
// 定义一个狗的类
class Dog extends Animal{
/* name: string;
age: number;
constructor(name: string,age: number){
this.name = name;
this.age = age;
}
sayHello(){
console.log('汪汪汪');
} */
// 子类中自己的方法
run(){
console.log(`${this.name}在跑...`);
}
}
const dog = new Dog('旺财',3)
console.log(dog);
dog.run();
// 定义一个猫的类
class Cat extends Animal{
sayHello(){
console.log('咩咩咩咩');
}
}
const cat = new Cat('tom',6)
console.log('cat',cat);
cat.sayHello();
})();
4.5、 类中的super关键字
-
如果在子类中写了构造函数,在子类构造函数中必须对父类的构造函数进行调用
-
在子类的构造函数中调用super函数,必须传入父类中的参数,否则语法错误;
// 立即执行函数,让变量拥有作用域就可以重复命名了
(() => {
// 定义一个Animal动物公共类,将dog和cat类中的内容定义在动物类中
class Animal{
name: string;
age: number;
constructor(name: string,age: number){
this.name = name;
this.age = age;
}
sayHello(){
console.log('动物在叫....');
}
}
/*
- 使用extends关键字,Dog子类继承父类Animal;
*/
// 定义一个狗的类
class Dog extends Animal{
/* name: string;
age: number;
constructor(name: string,age: number){
this.name = name;
this.age = age;
}
sayHello(){
console.log('汪汪汪');
} */
// 子类中自己的方法
run(){
console.log(`${this.name}在跑...`);
}
}
const dog = new Dog('旺财',3)
console.log(dog);
dog.run();
// 定义一个猫的类
class Cat extends Animal{
sayHello(){
console.log('咩咩咩咩');
}
}
const cat = new Cat('tom',6)
console.log('cat',cat);
cat.sayHello();
})();
4.6、TS中的抽象类
-
以abstract 关键字开头的类,是抽象类
-
抽象类和其他类区别不大,只是不能用来创建对象
-
抽象类就是专门用来被继承的类
(() => {
// 如何定义一个抽象类以及定义抽象方法
abstract class Animal{
name: string;
constructor(name: string){
this.name = name;
}
// 定义一个抽象方法, 方法“sayHello”不能具有实现,因为它标记为抽象。
abstract sayHello(): void;
}
// 继承一个动物类,如果不定义抽象类中是的抽象方法,ts就会报错
// 非抽象类“Dog”不会实现继承自“Animal”类的抽象成员“sayHello”。
class Dog extends Animal{
sayHello(){
console.log('abstract汪汪汪');
}
}
const dog1 = new Dog('小黑');
dog1.sayHello();
// const an = new Animal('小白') //无法创建抽象类的实例。ts(2511)
})();
5、TS 中的接口 interface&type
-
TS中的接口其实就是指约定一个对象的类型;
-
定义类时,可以使类去实现一个接口,使用implements关键字
(() => {
// 什么是接口
// console.log('接口');
/*
TS中的接口其实就是指约定一个对象的类型;
定义接口有两种方式:
① 使用 type 方式
② 使用interface 方式
*/
//① type 描述一个对象的类型
type myType = {
name: string,
age: number,
}
/** ① interface 接口形式描述一个对象的类型
* 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
* 同时接口也可以当成类型声明去使用
* 接口可以重复名字,重复名字定义的接口字段属性会叠加;
*/
interface myInterface{
name: string,
age: number,
}
interface myInterface{
sex:'男'
}
/** type & interface二者区别
* 接口可以在定义类的时候去限制类的接口,
* 接口中的所有属性都不能有实际的值,
* 接口只定义对象的结构,而不考虑实际的值
* 在接口中,所有的方法都是抽象方法;
*/
interface myInter{
// 都是抽象的
name: string;
sayHello():void;
}
/**
* 定义类时,可以使类去实现一个接口
* 实现接口就是使类满足接口的要求
* 使用implements关键字来实现接口继承
*/
// 什么叫接口,只要有看这个接口之后,才可以正常的数据通信;
// 实际接口就是对我们这个类的限制,制定规范,规定类型;
class myClass implements myInter{
name: string;
constructor(name: string){
this.name = name;
}
sayHello(): void {
console.log('大家好');
}
}
const obj: myInterface = {
name:'jack',
age:12,
sex:'男'
}
console.log(obj);
})();
6、TS 中属性的封装
-
属性的封装,目的就是让类中的属性访问时更加安全,不能直接修改需要通过类中提供的get和set方法来读取和修改;
-
public 指修饰的属性可以在任意位置访问和(修改) 是默认值
-
private 私有属性,私有属性只能在类内部访问(修改)
-
protected 受保护的属性,只能在当前类和当前类的子类中使用
(() => {
// 属性的封装
// 定义一个人的类
class Person{
// TS 可以在属性前添加属性的修饰符,来限制属性的访问以及修改
/**
* public 指修饰的属性可以在任意位置访问和(修改) 是默认值
* private 私有属性,私有属性只能在类内部访问(修改)
* ---通过在类中添加方法,使得私有属性可以被外部访问
* protected 受保护的属性,只能在当前类和当前类的子类中使用
*/
private _name: string;
private _age: number;
constructor(name: string,age: number){
this._name = name;
this._age = age;
}
/* 第二种方法,使用类中自带的属性存取器来设置访问和修改属性的方法
getter 方法用来读取属性
setter 方法用来设置属性
------- 它们被称为属性的存取器
*/
// TS 中设置getter方法的方式
get name(){
console.log('get name()执行了');
return this._name
}
// TS 中设置setter方法的方式
set name(value: string){
// console.log('set name()执行了');
this._name = value;
}
get age(){
return this._age
}
set age(value: number){
if(value < 0) return;
this._age = value;
}
// // 定义方法,用来获取name属性
// getName(){
// return this._name;
// }
// // 定义方法,用来设置name属性
// setName(value: string){
// this._name = value;
// }
// // 定义方法访问age属性
// getAge(){
// return this._age;
// }
// // 定义方法修改age;
// setAge(value: number){
// // 修改前就可以做判断,如果传入的值是负数,我们将不做任何修改
// if(value < 0) return;
// this._age = value
// }
}
// 实例化一个Person类
const per = new Person('孙悟空',18);
/**
* 一、现在属性是在对象中设置的,而且可以任意的被修改,
* 属性可以任意被修改将会导致对象中的数据变得非常不安全,所以我们要解决这个问题;
* 二、通过在class类内部设置访问和修改属性的方法,来间接安全的操作类中的属性,设置set和get方法;
*
*/
// 调用类中定义的方法,来访问或者修改name属性
// console.log(per.getName());
// per.setName('jack');
// console.log(per.getAge());
// per.setAge(-33) //传入的是负数,无法修改age值,达到我们需要的效果
// per._name = 'jack';
// per._age = -33; 属性“_age”为私有属性,只能在类“Person”中访问。
// 通过getter方法设置的方法,可以直接使用per.name,这句实际是在执行getter设置的方法,不是直接访问的属性
console.log(per.name);
per.name = 'jack';
console.log(per.age);
per.age = -33;
console.log('per:',per);
/* 三、protected 受保护的属性 */
class A{
protected num: number;
constructor(num: number){
this.num = num;
}
}
class B extends A{
test(){
console.log(this.num);
}
}
const b = new B(123);
// b.num = 33; //属性“num”受保护,只能在类“A”及其子类中访问。
/* 四、定义类中属性的简写方式 */
class C{
// 可以直接将属性定义在构造函数中
constructor(public name: string,public age: number){
}
}
/* 上述简写形式等价于下方代码 */
/* class C{
name: string;
age: number;
constructor(name: string,age: number){
this.name = name;
this.age = age;
}
} */
const c = new C('wangwu',12);
console.log('C',c);
})();
7、TS 中的泛型
-
泛型的作用就是在我们对类型不明确的时候,给我们创造一个变量,用这个变量来表示变量的类型,来达到约束类型的效果;
-
一、在定义函数或是类时,如果遇到类型不明确时,就可以使用泛型
-
二、泛型可以同时指定多个
-
三、T extends Inter 表示泛型必须是Inter的实现类(子类)
(() => {
// TS中的泛型
// 作用:是在我们对类型不明确的时候,给我们创造一个变量,用这个变量来表示变量的类型,来达到约束类型的效果
/* function fn(a: any): any{ //不确定a和fn返回值的类型,使用any可以解决问题,但是我们前面说过指定any类型的话会让ts失去类型的判断,所以这样做是❌
return a;
} */
/**
* 一、在定义函数或是类时,如果遇到类型不明确时,就可以使用泛型
*/
function fn<T>(a: T): T{
return a;
}
// 可以直接调用具有泛型的函数
let result = fn(10); // 不指定泛型,TS可以自动对类型进行推断;但有时候逻辑复杂时,也需要我们去指定泛型
console.log(result); //let result: number
// 指定泛型
let result2 = fn<string>('hello,泛型');
console.log(result2);
/* 二、泛型可以同时指定多个 */
function fn2<T, K>(a: T,b: K):T{
console.log(b);
return a;
}
// 调用多个泛型的函数
fn2<number, string>(999,'www');
console.log(fn2<number, string>(999,'www'));
/* 三、T extends Inter 表示泛型必须是Inter的实现类(子类) */
interface Inter{
length: number;
}
function fn3<T extends Inter>(a: T): number{
return a.length;
}
// fn3([]) 此处传入的参数必须要有length属性才可以,也就是说必须是Inter的实现类
class MyClass<T>{
name: T;
constructor(name: T){
this.name = name;
}
}
const mc = new MyClass<string>('孙悟空');
console.log('mc:',mc);
})();
五、 TS案例-------贪吃蛇小游戏
-
项目源码
略~