宠物网站设计模块,超值的扬中网站建设,网站建设三方协议,o2o免费网站建设初识Typescript
出现背景
Typescript#xff08;以下简称TS#xff09;实际上就是JavaScriptType#xff0c;用数据类型的方式来约束了JS的变量定义 在JS的基础上增加了类型支持 在JS中大多数错误都是因为数据类型造成的#xff0c;所以TS为了规避这个问题加入了类型限制…初识Typescript
出现背景
Typescript以下简称TS实际上就是JavaScriptType用数据类型的方式来约束了JS的变量定义 在JS的基础上增加了类型支持 在JS中大多数错误都是因为数据类型造成的所以TS为了规避这个问题加入了类型限制编译检查将问题在代码编译的时候代码执行前就可以发现错误
PSTS是微软开发的所以作为亲儿子配合VscodeTS 可以提前到在编写代码的同时就发现代码中的错误减少找 Bug、改 Bug 时间
几个中大型框架对TS的支持
Vue 3 源码使用 TS 重写Angular 默认支持 TSReact 与 TS 完美配合
TypeScript 已成为大中型前端项目的首先编程语言
快速起步
TS默认是浏览器无法认识的所以要通过编译器翻译成JS 新建一个文件夹在Vscode初始化环境
// 安装ts环境
npm i -g typescript
// 查看ts版本(验证是否安装成功)
tsc -v感受TS编译
新建一个hello.ts 的ts文件
通过命令编译ts文件生成一个js文件
tsc hello.ts此时在同级目录中会出现一个同名的 JS 文件执行js代码node .\hello.js
简化编译步骤
每次都这么手动编译太累了采用自动编译 简化方式使用 ts-node 包直接在 Node.js 中执行 TS 代码。 安装命令npm i -g ts-nodets-node 包提供了 ts-node 命令 使用方式ts-node hello.ts 解释ts-node 命令在内部偷偷的将 TS - JS然后再运行 JS 代码 报错解决方案
当然真正工程里初始化是完美自动化的具体实现可以自行百度
TS常用类型
前面我们知道了 TypeScript 是 JS 的超集TS 提供了 JS 的所有功能并且额外的增加了类型系统JS中如果number在运行过程中突然变成了boolean是很常见的所以也非常容易出问题
类型注解
在哪用类型注解
示例 如图所示代码中的 : number 就是类型注解。
目的就是为了给age加上类型的约束一旦变为其他的类型则报错 就会变成这样so约定了什么类型就只能给变量赋值该类型的值否则就会报错。
常用类型注解有哪些
可以将 TS 中的常用基础类型细分为两类1 JS 已有类型 2 TS 新增类型。
JS 已有类型 原始类型number/string/boolean/null/undefined/symbol。 对象类型object包括数组、对象、函数等对象。TS 新增类型 联合类型、自定义类型类型别名、接口、元组、字面量类型、枚举、void、any 等
原始类型
原始类型number/string/boolean/null/undefined/symbol。 这些按照上面的写法来做即可
数组类型
对象类型object包括数组、对象、函数等对象
数组的两种写法
//数组的定义方式这两种均可更推荐第一个
let numbers0: number[] [1, 2, 3];
let numbers1: Arraynumber [1, 2, 3];
//换了个数据类型
let numbers2: string[] [1, 2, 3];
let numbers3: Arraystring [1, 2, 3];需求数组里面我既要存number又要存string怎么办 用 | 来隔开数据类型
let numbers4: (number | string)[] [1, 2, 3];
let numbers5: Arraynumber | string [1, 2, 3];解释| 竖线在 TS 中叫做联合类型由两个或多个其他类型组成的类型表示可以是这些类型中的任意一种。 注意这是 TS 中联合类型的语法只有一根竖线不要与 JS 中的或||混淆了
类型别名
类型别名自定义类型为任意类型起别名。 使用场景当同一类型复杂被多次使用时可以通过类型别名简化该类型的使用相当于抽一个共通的类型出来 语法type 自定义类型名(类型1|类型2|.....)[]注意最后有个数组符号 例子
type userArray (number | string)[];
let numbers6: userArray [1, 2, 3];解释
使用 type 关键字来创建类型别名。类型别名比如此处的 CustomArray可以是任意合法的变量名称。创建类型别名后直接使用该类型别名作为变量的类型注解即可。
这里补一个知识用type类型是可以定义方法头部的类似接口 type 函数名 (param1: number, param2: string) ReturnType;
//先用type来定义方法的参数列表箭头函数后面是方法的返回值类型
//type 函数名 (param1: number, param2: string) ReturnType;
type F1 (a: number) number
// 定义f1类型用F1约束好利用箭头函数实现方法体
// 注意这里只是按照type的约束把方法定义出来调用的时候需要实例化或者去单独调用
let f1: F1 (a: number):number {// your code ...return a 1;
}
//方法调用
let result f1(111)
console.log(result)函数类型类似方法定义
函数的类型实际上指的是函数参数和返回值的类型。 为函数指定类型的两种方式1 单独指定参数、返回值的类型 2 同时指定参数、返回值的类型。
单独指定参数、返回值的类型
语法
function 函数名(参数1: 类型, 参数2: 类型): 返回值类型{//方法体return 返回值如果注明了返回值类型的话就代表有返回值;
}//这两种定义方法的方法结果是一样的区别在于定义的方式不同
//function是原始的js玩法const是用的箭头函数调用的时候也完全一样
function add(param1: number, param2: number): number {return param1 param2;
}const add1 (param1: number, param2: number): number {return param1 param2;
}add(1, 2)
add1(1, 2)同时指定参数、返回值的类型
同时指定参数、返回值的类型
const add2: (param1: number, param2: number) number (param1, param2) {return param1 param2;
}解释当函数作为表达式时可以通过类似箭头函数形式的语法来为函数添加类型。 注意这种形式只适用于函数表达式
无返回值类型
如果函数没有返回值那么函数返回值类型为void
function f1(name: string): void {console.log(userName:, name)
}可选参数
使用函数实现某个功能时参数可以传也可以不传。这种情况下在给函数参数指定类型时就用到可选参数了。 比如数组的 slice 方法可以 slice() 也可以 slice(1) 还可以 slice(1, 3) 自定义一个测试类
function f2(name1?: string, name2?: string): void {console.log(name1:, name1, name2:, name2);
}
//调用时传不传参数都可以
f2(aa, bb);
f2(aa);可选参数在可传可不传的参数名称后面添加 ?问号 注意可选参数只能出现在参数列表的最后也就是说可选参数后面不能再出现必选参数。也就是function f2(name1: string, name2?: string):void{ ... }
对象类型
JS 中的对象是由属性和方法构成的而 TS 中对象的类型就是在描述对象的结构有什么类型的属性和方法。 对象类型的写法
let obj1: {name: string,sayHi(): void} {name: 张三,sayHi(): void { console.log(Hi~) },
}解释
直接使用 {} 来描述对象结构。属性采用属性名: 类型的形式方法采用方法名(): 返回值类型的形式。如果方法有参数就在方法名后面的小括号中指定参数类型比如greet(name: string): void。在一行代码中指定对象的多个属性类型时使用 ;分号来分隔。 如果一行代码只指定一个属性类型通过换行来分隔多个属性类型可以去掉 ;分号。 方法的类型也可以使用箭头函数形式比如{ sayHi: () void }。
再扩展几个方法
let obj1: {name: string,age: number,sayHi(): void,sayName(name: string): void
} {name: 张三,age: 18,sayHi(): void { console.log(Hi~) },sayName(name: string): void { console.log(name:, name) }
}对象类型(参数可选情况)
对象的属性或方法也可以是可选的此时就用到可选属性了。 比如我们在使用 axios({ … }) 时如果发送 GET 请求method 属性就可以省略。
function myAxios(config: { url: string, method?: string }) {//灵活应用如果发送 GET 请求method 属性就可以省略//打印的时候如果没传对应的参数就不打印了console.log(config)
}可选属性的语法与函数可选参数的语法一致都使用 ?问号来表示
接口
当对象类型被多次使用时一般会使用接口interface来描述对象的类型来达到复用的目的。 类似继承方法不实现是不可以的属性方法都要具体定义。 修改后对变量方法做具体实现
// 定义接口定义好属性
interface IPerson {name: string;age: number;sayHi(): void;
}
//使用接口类型约束后需要具体实现
const obj: IPerson {name: 张三,age: 18,sayHi(){console.log(Hi~)}
}解释
使用 interface 关键字来声明接口。接口名称比如此处的 IPerson可以是任意合法的变量名称。声明接口后直接使用接口名称作为变量的类型。因为每一行只有一个属性类型因此属性类型后没有 ;分号。
interface和type的区别
接口更灵活 相同点都可以给对象指定类型。
不同点接口只能为对象指定类型。类型别名不仅可以为对象指定类型实际上可以为任意类型指定别名。
接口复用
如果两个接口之间有相同的属性或方法可以将公共的属性或方法抽离出来通过继承来实现复用。 原有状态
interface Point2D {x: number;y: number;
}interface Point3D {x: number;y: number;z: number;
}比如这两个接口都有 x、y 两个属性重复写两次可以但很繁琐。 所以就引出了接口复用。 直接用extends来复用2D里面的内容
interface Point3D extends Point2D {z: number;
}
//集成后对值进行实现
const a: Point3D {x: 1,y: 2,z: 3,
}解释
使用 extends继承关键字实现了接口 Point3D 继承 Point2D。继承后Point3D 就有了 Point2D 的所有属性和方法此时Point3D 同时有 x、y、z 三个属性。
元组
场景在地图中使用经纬度坐标来标记位置信息。 可以使用数组来记录坐标那么该数组中只有两个元素并且这两个元素都是数值类型。
//这样标记不严谨没有具体约束有多少个元素
let position: number[] [1, 2]
//使用元组进行约束固定两个number元素的数组(当然其他元素也完全可以随便搞)
let position1: [number, number] [1, 2]元组类型是另一种类型的数组它确切地知道包含多少个元素以及特定索引对应的类型
解释
元组类型可以确切地标记出有多少个元素以及每个元素的类型。该示例中元素有两个元素每个元素的类型都是 number。
类型推断
TS 中某些没有明确指出类型的地方TS 的类型推论机制会帮助提供类型。 换句话说由于类型推论的存在这些地方类型注解可以省略不写 发生类型推论的 2 种常见场景1 声明变量并初始化时 2 决定函数返回值时。 鼠标放在上面自动推断
类型断言
有时候你会比 TS 更加明确一个值的类型此时可以使用类型断言来指定更具体的类型。我断言这是一个xxx的类型 用途一般都是获取某某标签然后通过断言来获取标签属性 注意getElementById 方法返回值的类型是 HTMLElement该类型只包含所有标签公共的属性或方法不包含 a 标签特有的 href 等属性。 因此这个类型太宽泛不具体无法操作 href 等 a 标签特有的属性或方法。 解决方式这种情况下就需要使用类型断言指定更加具体的类型。
使用类型断言 明确的指明元素的类型 解释
使用 as 关键字实现类型断言。关键字 as 后面的类型是一个更加具体的类型HTMLAnchorElement 是 HTMLElement 的子类型。通过类型断言aLink 的类型变得更加具体这样就可以访问 a 标签特有的属性或方法了。 通过控制台打印 字面量类型
首先看下面两个变量类型 并不是都是string类型的
let str1Hello TS
const str2Hello TS!通过 TS 类型推论机制可以得到答案
变量 str1 的类型为string。变量 str2 的类型为‘Hello TS’。
解释 3. str1 是一个变量let它的值可以是任意字符串所以类型为string。 4. str2 是一个常量const它的值不能变化只能是 ‘Hello TS’所以它的类型为‘Hello TS’。这个变量锁死了就只能是**‘Hello TS’**const代表常量不可变更
注意此处的 ‘Hello TS’就是一个字面量类型。也就是说某个特定的字符串也可以作为 TS 中的类型。 除字符串外任意的 JS 字面量比如对象、数字等都可以作为类型使用。
使用模式字面量类型配合联合类型一起使用。 使用场景用来表示一组明确的可选值列表。 比如在贪吃蛇游戏中游戏的方向的可选值只能是上、下、左、右中的任意一个这里用到了枚举。
//这里用了枚举所以changeDirection的参数只能传up | down | left | right中的一个
function changeDirection(direction: up | down | left | right) {console.log(direction)
}
//只能传up | down | left | right中的一个
changeDirection(up);严格的类型约束
枚举
基础使用
上面字面量的场景当字面量很多的情况下就会显得很冗余 这里就换成枚举
枚举的功能类似于字面量类型联合类型组合的功能也可以表示一组明确的可选值。 枚举定义一组命名常量。它描述一个值该值可以是这些命名常量中的一个。 使用 enum 关键字定义枚举。约定枚举名称、枚举中的值以大写字母开头。枚举中的多个值之间通过 ,逗号分隔。定义好枚举后直接使用枚举名称作为类型注解。
调用过程中因为标记了只能传入枚举类型so传入的时候只能枚举.值
数字枚举
为什么叫数字枚举 不难发现枚举本身是没有对值进行定义的 而枚举的默认类型是number并且第一个元素默认的值是0 第n个元素的默认值是n-1 这是数字枚举的默认情况当然我们可以像Java里的枚举一样进行默认值赋值处理。 如果有的枚举没有赋值那么他就会默认继续自增下去
字符串枚举
看字面量就可以知道字符串枚举内部的类型都是字符串需要默认就给赋值不赋值就会被默认为数字枚举 如果有其中一个没有赋值就会报错因为 字符串枚举没有自增长行为因此字符串枚举的每个成员必须有初始值。
枚举编译后的样子 any类型
首先说明any类型不推荐在TS使用因为any会把TS变成anyScript 当一个变量被标记为any的时候那么将不再会对其有任何提示 当用any标记之后退化成JS了属于是 编译才会发现错误违背了TS的早发现初衷 尽可能的避免使用 any 类型除非临时使用 any 来“避免”书写很长、很复杂的类型 其他隐式具有 any 类型的情况 1 声明变量不提供类型也不提供默认值 2 函数参数不加类型。 注意因为不推荐使用 any所以这两种情况下都应该提供类型
typeof 实际上TS 也提供了 typeof 操作符可以在类型上下文中引用变量或属性的类型类型查询。 使用场景根据已有变量的值获取该值的类型来简化类型书写
未简化前状态 既然上面的let p和方法里面的point对象是一个类型的那么就可以用typeof简化 这里需要指明关键字直接标记是不可以的 优化后使用typeof关键字 解释
使用 typeof 操作符来获取变量 p 的类型结果与第一种对象字面量形式的类型相同。typeof 出现在类型注解的位置参数名称的冒号后面所处的环境就在类型上下文区别于 JS 代码。注意typeof 只能用来查询变量或属性的类型无法查询其他形式的类型比如函数调用的类型
TS高级类型
class类
这个和Java类差不多
TypeScript 全面支持 ES2015 中引入的 class 关键字并为其添加了类型注解和其他语法比如可见性修饰符等 根据 TS 中的类型推论可以知道 Person 类的实例对象 p 的类型是 Person。TS 中的 class不仅提供了 class 的语法功能也作为一种类型存在。
class类初始化 class构造函数 解释
成员初始化比如age: number后才可以通过 this.age 来访问实例成员。需要为构造函数指定类型注解否则会被隐式推断为 any构造函数不需要返回值类型。
此时再新建对象就可以直接指定值
class类的方法
在class类中提供一些方法创建完成之后通过创建对象调用 解释方法的类型注解参数和返回值与函数用法相同正常传参调用即可。当然那个void可以不写直接靠return自动类型推断也完全OK
class类继承extends
这个是js就自带的通过继承类就可以调用父类的方法通过继承获取父类的所有属性以及方法 解释
通过 extends 关键字实现继承。子类 Dog 继承父类 Animal则 Dog 的实例对象 dog 就同时具有了父类 Animal 和 子类 Dog 的所有属性和方法。Dog类新建对象调用方法
class类实现implements
这个是ts提供的通过interface定义类用implements来实现在子类中对方法进行具体实现 解释
通过 implements 关键字让 class 实现接口。sing类实现接口 Singable 意味着sing 类中必须提供 Singable 接口中指定的所有方法和属性。
class类访问权限控制
相比于Java的四种控制权限TS只有3种少了Java的default 类成员可见性可以使用 TS 来控制 class 的方法或属性对于 class 外的代码是否可见。 可见性修饰符包括1 public公有的 2 protected受保护的 3 private私有的。
public公有的
public表示公有的、公开的公有成员可以被任何地方访问默认可见性 在哪都能访问到就不具体演示了 解释
在类属性或方法前面添加 public 关键字来修饰该属性或方法是共有的。因为 public 是默认可见性所以可以直接省略。
protected受保护的
protected表示受保护的仅对其声明所在类和子类中非实例对象可见。 解释
在类属性或方法前面添加 protected 关键字来修饰该属性或方法是受保护的。在子类的方法内部可以通过 this 来访问父类中受保护的成员但是对实例不可见
private私有的
private表示私有的只在当前类中方法内可见对实例对象以及子类也是不可见的。 解释
在类属性或方法前面添加 private 关键字来修饰该属性或方法是私有的。私有的属性或方法只在当前类中的方法可见对子类和实例对象也都是不可见的
readonly只读修饰符
除了可见性修饰符之外还有一个常见修饰符就是readonly只读修饰符。 readonly表示只读用来防止在构造函数之外对属性进行赋值。 解释
使用 readonly 关键字修饰该属性是只读的注意只能修饰属性不能修饰方法。注意属性 age 后面的类型注解比如此处的 number如果不加则 age 的类型为 18 字面量类型。接口或者 {} 表示的对象类型也可以使用 readonly来修饰变量
类型兼容性
先说个梗如果一个东西走路像鸭子长得像鸭子吃饭像鸭子那他就是个鸭子 两种类型系统1 Structural Type System结构化类型系统 2 Nominal Type System标明类型系统。
为了帮助理解这种其实是类似于多态很大程度上利用了class、方法的兼容
Structural Type System结构化类型系统
TS 采用的是结构化类型系统也叫做 duck typing鸭子类型类型检查关注的是值所具有的形状。 也就是说在结构类型系统中如果两个对象具有相同的形状则认为它们属于同一类型它看起来就是鸭子。
这两个可以被认为是同一类型
那么point和point2的两个对象的结构是相同的因此认为是“同一类型”在new对象的时候就可以这样
//这里之所以可以这么创建是因为point和point2的两个对象被认为是同一类型
//这里point代表p的类型point2()代表创建point2类型的实例
const p: point new point2()解释
Point 和 Point2D 是两个名称不同的类。变量 p 的类型被显示标注为 Point 类型但是它的值却是 Point2D 的实例并且没有类型错误。因为 TS 是结构化类型系统只检查 Point 和 Point2D 的结构是否相同结构相同就认为是同一类型相同都具有 x 和 y 两个属性属性类型也相同。但是如果在 Nominal Type System 中比如C#、Java 等它们是不同的类类型无法兼容。
结构化类型系统向下兼容
注意在结构化类型系统中如果两个对象具有相同的形状则认为它们属于同一类型这种说法并不准确。 更准确的说法对于对象类型来说y 的成员至少与 x 相同则 x 兼容 y成员多的可以赋值给少的。 反过来的话肯定不兼容 解释
Point3D 的成员至少与 Point 相同则 Point 兼容 Point3D。所以成员多的 Point3D 可以赋值给成员少的 Point只要能全部满足少的一方多加多少都可以。
接口兼容性
除了 class 之外TS 中的其他类型也存在相互兼容的情况包括1 接口兼容性 2 函数兼容性 等。 接口之间的兼容性类似于 class。并且class 和 interface 之间也可以兼容。
interface之间可以直接使用类型兼容写法和class一样 interface与class之间也可以兼容
函数兼容性
函数之间兼容性比较复杂需要考虑1 参数个数 2 参数类型 3 返回值类型。 参数个数参数多的兼容参数少的或者说参数少的可以赋值给多的和对象刚好相反。
//用type来定义函数
type 函数名 (param1: number, param2: string) ReturnType;//先用type来定义方法的参数列表箭头函数后面是方法的返回值类型
//type 函数名 (param1: number, param2: string) ReturnType;
type F1 (a: number) void
// 定义f1类型用F1约束好利用箭头函数实现方法体
// 注意这里只是定义调用需要实例化或者单独调用方法
let f1: F1 (a: number): void {// your code ...
}
// 定义F2的方法体比F1的要多一个参数
type F2 (a: number, b: number) void
//将F1的方法比F2的方法要少一个参数所以可以兼容
let f2: F2 f1
//方法调用
let result f2(111)
console.log(result)再比如说数组的forEach方法可以选择传一个参数也可以一个不传 解释 参数少的可以赋值给参数多的所以f1 可以赋值给 f2。数组 forEach 方法的第一个参数是回调函数该示例中类型为(value: string, index: number, array: string[]) void。在 JS 中省略用不到的函数参数实际上是很常见的这样的使用方式促成了 TS 中函数类型之间的兼容性。并且因为回调函数是有类型的所以TS 会自动推导出参数 item、index、array 的类型。 参数类型相同位置的参数类型要相同原始类型或兼容对象类型 解释函数类型 F2 兼容函数类型 F1因为 F1 和 F2 的第一个参数类型相同。
来个复杂点的 先用interface定义参数类型 在type定义参数列表的时候用上interface做定义 先定义了f2再去赋给f3但是f3反过来赋给f2不可以 因为参数列表无法 从小范围 的去兼容 大范围 解释
注意此处与前面讲到的接口兼容性冲突。技巧将对象拆开把每个属性看做一个个参数则参数少的f2可以赋值给参数多的f3。 返回值类型只关注返回值类型本身即可 解释
如果返回值类型是原始类型此时两个类型要相同比如左侧类型 F5 和 F6。如果返回值类型是对象类型此时成员多的可以赋值给成员少的比如右侧类型 F7 和 F8。
交叉类型
交叉类型()有点类似于接口继承extends用于多个类型组合为一个类型
//定义两个接口类型
interface Person {name: string;
}
interface Man {age: string;
}
//用type来交叉两个类型
type PersonAndMan Person Man;
const p: PersonAndMan {//获取到Person的name属性name: zhangsan,//获取到Man的age属性age: lisi,
};解释当使用了交叉类型之后type PersonAndMan就拥有了Person和Man的两个类型 交叉类型与继承extends的对比
相同点都可以实现对对象类型的组合不同点两种方式实现类型组合时对于同名属性之间出现冲突如果同名同属性则视为同一个如果同名不同属性则会冲突处理类型冲突的方式不同 泛型
初识泛型
泛型时可以保证类型安全的前提下让函数等与多种类型一起工作从而实现灵活复用常用于函数、接口、class中
需求创建一个id函数传入什么数据就返回数据本身参数和返回值类型一样 比如这个函数只能传递number类型并且返回number类型无法用于其他类型
function id(param: number): number{return param;
}要是稍微改造一下把类型换成any。这样确实可以接受其他类型但代价是失去了TS的类型保护不安全
function id(param: any): any {return param;
}
此时的解决方案就是泛型 泛型再保证类型安全不丢失类型信息的同时可以让函数可以传入多种不同的类型实现灵活复用。
创建一个泛型函数 实操一下
//将函数得参数类型定义为Type
function testType(value: Type): Type {return value;
}
//同样的函数传入不同的类型不会报错完成同一个函数复用的目的
const m1: number test(20);
const m2: string test(aa);
const m3 test(20);
const m4 test(aa);
解释 1.再调用泛型函数时可以省略类型来简化泛型函数的调用 2.此时TS的内部会采用类型参数推断机制来根据传入的实参自动推断出类型变量Type的类习惯 3.比如此时传入实参20TS会自动推断出传入的参数时number此时Type就会自动变成number类型 推荐使用这种简化的方式调用泛型函数使代码更短更易于阅读 说明当编译器无法推断类型或者推断类型不够准确的时候就需要显式的传入参数类型参数
泛型约束
问题描述 默认情况下由于Type类型可以代表的类型太多导致无法访问任何属性
泛型收缩的方式
添加泛型约束来收缩类型主要有两种方式1 指定更加具体的类型 2 添加约束
1.指定更加具体的类型
function test2Type(value: Type[]): Type[] {console.log(value.length);return value;
}比如将类型修改为Type[]Type类型的数组因为只要是数组就一定存在length属性因此就可以访问了。
2.添加约束 创建 描述约束的接口ILength该接口要求提供length属性 通过继承extends关键字来使用该接口为泛型类型变量添加约束 该约束表示传入的类型中必须有length属性 注意这里传入的实参比如数组对象只要有length属性即可并且也符合前面讲到的接口类型兼容性
//指定一个number类型的Type
interface ILength {length: number;
}
//Type利用extends来约束类型
function test3Type extends ILength(param: Type): Type {console.log(param.length);return param;
}多个类型变量
类型变量间约束 泛型接口
//定义接口类型为Type,内部的类型都可以根据Type来变化
interface IdFuncType {id: (value: Type) Type;ids: () Type[];
}
//新建变量实现接口接口传入number
let obj: IdFuncnumber {id(value) {return value;},ids() {return [1, 3, 5];},
};
//调用方法...
obj.id(1);解释 1.在interface后面添加类型变量那么该接口就变成了泛型接口 2.interface的类型变量对接口中所有其他成员都可见也就是接口中所有成员都可以用类型变量 3.使用泛型接口的时候需要显式的标注具体类型比如interface IdFuncType {...} 4.通过变量实现接口后let obj: IdFuncnumber {}obj的变量和方法都会受到传入number变量的影响。 比如 id: (value: Type) Type;在obj的实现中就会变成 id: (value: number) number; 比如 ids: () number[];在obj的实现中就会变成 ids: () number[];
实际上JS中的数组在TS中就是一个泛型接口根据传入的不同类型变化为不同的数组类型 每次foreach的方法参数类型也会不一样
泛型类
class也可以配合泛型来使用
比如React中的class组件的基类Component就是泛型类不同的组件有不同的props和state 创建一个泛型类 在创建类型的时候明确指定创建对象类型 解释 1.类似于泛型接口在class名称后面添加 类型变量这个类就变成了泛型类 2.此处的add方法采用的是箭头函数形式的类型书写方法
泛型工具类
泛型工具类型TS 内置了一些常用的工具类型来简化 TS 中的一些常见操作。 说明它们都是基于泛型实现的泛型适用于多种类型更加通用并且是内置的可以直接在代码中使用。 这些工具类型有很多主要学习以下几个
PartialType
ReadonlyType
PickType, Keys
RecordKeys, TypePartial Type可选
泛型工具类型Partial用来构造创建一个类型将 Type 的所有属性设置为可选 构造出来的新类型 PartialProps 结构和 Props 相同但所有属性都变为可选的
interface A {id: stringchildren: number[]
}
//定义Props的Type结构与interface A一致
//但是所有属性都变为可选的
type Props PartialA
//定义obj对象结构可以与A一样
let obj: Props {id: 123,children: [1, 2, 3]
}ReadOnly Type只读
泛型工具类型 - Readonly 用来构造一个类型将 Type 的所有属性都设置为 readonly只读。 构造出来的新类型 ReadonlyProps 结构和 A 相同
PickType, Keys可选
泛型工具类型 PickType, Keys 从 Type 中选择一组属性来构造新类型
interface A {id: stringage: stringchildren: number[]
}
//定义一个Pick的类选择A接口中的两个属性
type ReadonlyType PickA, id | age
//到这里为止实际上只能使用A接口的id与age属性
let obj: ReadonlyType {id: 111,age: 18
}
如果强行加入只会报错
Pick 工具类型有两个类型变量1 表示选择谁的属性 2 表示选择哪几个属性。其中第二个类型变量如果只选择一个则只传入该属性名即可。第二个类型变量传入的属性只能是第一个类型变量中存在的属性。构造出来的新类型 PickProps只有 id 和 title 两个属性类型。
RecordKeys,Type构造
泛型工具类型RecordKeys,Type 构造一个对象类型属性键为 Keys属性类型为 Type。
Record 工具类型有两个类型变量1 表示对象有哪些属性 2 表示对象属性的类型。
//用Record来记录类型
//规定RecordObj有a | b | c三个属性均为string数组
type RecordObj Recorda | b | c, string[]
let obj: RecordObj {a: [1],b: [2],c: [3]
}
//规定RecordObj2有a | b | c三个属性均为string类型
type RecordObj2 Recorda | b | c, string
let obj2: RecordObj2 {a: 1,b: 2,c: 3
}索引签名类型
绝大多数情况下我们都可以在使用对象前就确定对象的结构并为对象添加准确的类型。 使用场景当无法确定对象中有哪些属性或者说对象中可以出现任意多个属性此时就用到索引签名类型了。 解释
使用 [key: string] 来约束该接口中允许出现的属性名称。表示只要是 string 类型的属性名称都可以出现在对象中。number代表属性只能赋number类型这样对象 obj 中就可以出现任意多个属性比如a、b 等。key 只是一个占位符可以换成任意合法的变量名称。隐藏的前置知识JS 中对象{}的键是 string 类型的。
数组索引签名
在 JS 中数组是一类特殊的对象特殊在数组的键索引是数值类型。 并且数组也可以出现任意多个元素。所以在数组对应的泛型接口中也用到了索引签名类型。 解释
MyArray 接口模拟原生的数组接口并使用 [n: number] 来作为索引签名类型。该索引签名类型表示只要是 number 类型的键索引都可以出现在数组中或者说数组中可以有任意多个元素。同时也符合数组索引是 number 类型这一前提。
映射类型
映射类型基于旧类型创建新类型对象类型减少重复、提升开发效率。(简单说就是把属性名都给copy过来具体类型重新定义) 解释
映射类型是基于索引签名类型的所以该语法类似于索引签名类型也使用了 []。Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个类似于 forin(let k in obj)。使用映射类型创建的新对象类型 Type2 和类型 Type1 结构完全相同。注意映射类型只能在类型别名中使用不能在接口中使用。
其他的一些情况
TypeScript 类型声明文件
今天几乎所有的 JavaScript 应用都会引入许多第三方库来完成任务需求。 这些第三方库不管是否是用 TS 编写的最终都要编译成 JS 代码才能发布给开发者使用。 我们知道是 TS 提供了类型才有了代码提示和类型保护等机制。 但在项目开发中使用第三方库时你会发现它们几乎都有相应的 TS 类型这些类型是怎么来的呢类型声明文件 类型声明文件用来为已存在的 JS 库提供类型信息。 这样在 TS 项目中使用这些库时就像用 TS 一样都会有代码提示、类型保护等机制了。
TS中的两种文件类型
TS 中有两种文件类型1 .ts 文件 2 .d.ts 文件。 .ts 文件
既包含类型信息又可执行代码。可以被编译为 .js 文件然后执行代码。用途编写程序代码的地方。
.d.ts 文件
只包含类型信息的类型声明文件。不会生成 .js 文件仅用于提供类型信息。用途为 JS 提供类型信息。
总结.ts 是 implementation代码实现文件.d.ts 是 declaration类型声明文件。
如果要为 JS 库提供类型信息要使用 .d.ts 文件
使用已有的类型声明文件 1 内置类型声明文件 2 第三方库的类型声明文件。
库自带类型声明文件 由 DefinitelyTyped 提供
自己创建声明文件并使用
项目内共享类型
如果多个 .ts 文件中都用到同一个类型此时可以创建 .d.ts 文件提供该类型实现类型共享。
操作步骤
创建 utils.d.ts 类型声明文件。创建需要共享的类型并使用 export 导出TS 中的类型也可以使用 import/export 实现模块化功能。在需要使用共享类型的 .ts 文件中通过 import 导入即可.d.ts 后缀导入时直接省略。
为已有 JS 文件提供类型声明 相当于加载js文件时自动转换为.d.ts文件
React中使用TS
使用 CRA 创建支持 TS 的项目
看起来CRA是什么很屌的工具是吧实际不是 说白了就是个一件创建React工程的 React 脚手架工具 create-react-app简称CRA默认支持 TypeScript。 创建支持 TS 的项目命令create-react-app my-react-app 项目名称my-react-app。 当看到以下提示时表示支持 TS 的项目创建成功
相比于非TS的项目的区别
相对于非 TS 项目目录结构主要由以下三个变化
项目根目录中增加了 tsconfig.json 配置文件指定 TS 的编译选项比如编译时是否移除注释。React 组件的文件扩展名变为*.tsx。src 目录中增加了 react-app-env.d.tsReact 项目默认的类型声明文件。 解读一下react-app-env.d.tsReact 项目默认的类型声明文件。 三斜线指令指定依赖的其他类型声明文件types 表示依赖的类型声明文件包的名称 解释告诉 TS 帮我加载 react-scripts 这个包提供的类型声明。 react-scripts 的类型声明文件包含了两部分类型react、react-dom、node 的类型图片、样式等模块的类型以允许在代码中导入图片、SVG 等文件。 TS 会自动加载该 .d.ts 文件以提供类型声明通过修改 tsconfig.json 中的 include 配置来验证
TS配置文件tsconfig.json
tsconfig.json 指定项目文件和项目编译所需的配置项。 注意TS 的配置项非常多100以 CRA 项目中的配置为例来学习其他的配置项用到时查文档即可。
tsconfig.json 文件所在目录为项目根目录与 package.json 同级。tsconfig.json 可以自动生成命令tsc --init。一般需要手动生成
可以看到很多的配置被注掉选择需要的解开即可 除了在 tsconfig.json 文件中使用编译配置外还可以通过命令行来使用。 使用演示tsc hello.ts --target es6。 这个的意思是 编译hello.ts文件以es6的版本编译 注意
tsc 后带有输入文件时比如tsc hello.ts将忽略 tsconfig.json 文件。tsc 后不带输入文件时比如tsc才会启用 tsconfig.json。 推荐使用tsconfig.json 配置文件。
React 中的常用类型
React 是组件化开发模式React 开发主要任务就是写组件 两种组件1 函数组件 2 class 组件。
函数组件主要包括以下内容
组件的类型组件的属性props组件属性的默认值defaultProps事件绑定和事件对象
函数组件
函数组件创建
注意react的组件用的是.tsx文件如果只用.ts文件是用不了H5的组件的
一定一定要看一下这个文章ReactTS工程初始化的问题
import React, { FC } from react;
import ReactDOM from react-dom;//定义一个Props的接口
interface Props {name: string;age: number;
}//Hello组件允许传入Props类型参数
//其实不写React.FC也可以。React.FC表示React.Function Component。
//React.FC 显式地定义了返回类型作为一个组件返回其他方式是隐式推导的。
const Hello: React.FCProps ({ name, age }) (div名字{name}年龄{age}/div
)
//Test组件没有参数
const Test: React.FC () (div这里是一个Test组件/div
)//创建App作为整个页面的基础
const App () {return (div{/* 组件传值 */}Hello name{zs} age{18} /Test/Test/div)}
//将App /渲染到root上
ReactDOM.render(App /, document.getElementById(root))关于那个传参的还是可以再简化一下
函数组件属性的默认值defaultProps
第一种写法有React.FC的显式标注比较麻烦换第二种
// 比如我想给组件的某个属性赋一个固定值
const Hello: React.FCProps ({ name, age }) (div名字{name}年龄{age}/div
)
//赋默认值
Hello.defaultProps {age: 18
}第二种写法直接在参数列表上赋值完全按照函数在 TS 中的写法
//直接在参数上赋值
const Hello ({ name, age 18 }: Props) (div名字{name}年龄{age}/div
)事件绑定和事件对象
事件绑定和事件对象
//Test组件没有参数
const Test: React.FC () (div{/* 绑定点击操作 */}button onClick{onclick}点击/button/div
)
//绑定事件
const onclick () {console.log(点击操作)
}
//这里绑定的是鼠标事件操作
const onclick1 (e: React.MouseEventHTMLButtonElement, MouseEvent) {console.log(点击操作)
}//创建App作为整个页面的基础
const App () {return (divTest/Test/div)
}
//将App /渲染到root上
ReactDOM.render(App /, document.getElementById(root))绑定事件写法 再比如文本框
//Test组件没有参数
const Test: React.FC () (div{/* 绑定修改输入框操作 */}input onChange{onchange} //div
)
//这里绑定的是修改事件操作
const onchange (e: React.ChangeEventHTMLInputElement) {console.log(输入框发生变化)
}查看可以绑定的事件 实际操作 确定好可以绑定的事件之后就可以进行事件绑定
//这里绑定的是输入变化操作
const onchange (e: React.ChangeEventHTMLInputElement) {console.log(输入框发生变化)
}class组件
class 组件主要包括以下内容 组件的类型、属性、事件 props 组件状态state
class组件属性props
Props属性 props 是 React 组件的一种机制用于向组件传递数据。它是从父组件传递给子组件的数据而子组件不能直接修改 props只能读取其中的数据。因此props 是用于组件之间通信的一种方式。 在使用组件时可以在组件标签上添加属性这些属性将被封装成 props 对象传递给组件。在组件内部通过解构或直接访问 props 对象可以获取传递的数据然后在组件中使用这些数据。
//定义一个Props的接口
type A {name: string;age?: number;
}
// React.ComponentProps,State这里只传入Props不传入State
class Test extends React.ComponentA, {} {//将A的age属性赋默认值static defaultProps: PartialA {age: 18}render() {//通过this.props获取值当然也可以在这上面直接赋值const { name, age 20 } this.props;return div名字{name}年龄{age}/div/}
}//创建App作为整个页面的基础
const App () {return (divTest/Test/div)}
//将App /渲染到root上
ReactDOM.render(App /, document.getElementById(root))class组件状态state
State状态 state 是 React 组件用于管理自己的内部状态的一种机制。通过使用 useState 或 useReducer 等 React 提供的钩子或类组件的 setState 方法可以在组件内部创建和管理状态。 与 props 不同state 是组件私有的只能在组件内部访问和修改。当 state 发生改变时React 将会自动更新组件并重新渲染显示新的状态。
//定义State名字的type组件
type State {count: number
}class Counter extends React.Component{}, State{//将State赋默认值static: State {count: 20//number: 30 因为没有State没有number所以无法赋值}//定义方法state是组件内部使用的onIncrement () {this.setState({//改变count的值count: this.state.count 1})}render() {//这里定义返回组件调用组件定义的自增函数return divbutton onClick{this.onIncrement}1/button/div/}
}//创建App作为整个页面的基础
const App () {return (divCounter/Counter/div)
}
//将App /渲染到root上
ReactDOM.render(App /, document.getElementById(root))总结React.Component 的 Props和State
props 是组件之间进行数据传递的一种机制用于从父组件向子组件传递数据。 state 是组件内部维护的状态用于管理组件的变化和更新。
props 是只读的组件不能直接修改传递给它的 props。 state 是可变的可以通过特定的方法修改组件的 state触发组件的重新渲染。 对于React.Component来说参数是可选的 定义两个参数type
type State {count: number
}
type Props {message: string
}