React-TypeScript进阶使用
【摘要】 React-TypeScript进阶使用
❤ React-TypeScript进阶使用
约束
的思想
接下来看看如何进一步具体的对于我们React之中TS的类型做出约束呢
1、函数类型
就是函数参数和返回值加了一个类型
简单写个函数,然后我们调用一下
const handleTs = (numA,numB) => {
console.log(numA,numB,'numA,numB');
}
handleTs(1,2);
1 2 'numA,numB'
接下来我们加个约束类型:
const handleTs = (numA:number,numB:number) => {
console.log(numA,numB,'numA,numB');
return numA+numB;
}
加个返回值的约束类型:
const handleTs = (numA:number,numB:number) : number => {
console.log(numA,numB,'numA,numB');
return numA+numB;
}
let total=handleTs(1,2);
console.log(total,'total');
再抽离出来添加类型别名
type TsTotal = (numA:number,numB:number) => number
const handleTs:TsTotal =(numA,numB) =>{
console.log(numA,numB,'numA,numB');
return numA+numB;
}
let total=handleTs(1,2);
console.log(total,'total');
上面就是我们函数有返回值时候的写法
那么没有返回值呢? 这个时候就需要用到void 类型
const handleTs =(numA:number): void =>{
console.log(numA,'numA');
}
let total=handleTs(1);
console.log(total,'total');
当然了,void和不写时候也是一样的。
也就是说下面的两种写法其实都是一样的
//什么都不写,返回值类型为: void
const handleTs = () => {}
// 指定函数返回值类型为 void
const handleTs = (): void => {}
指定返回值类型为 undefined的情况
// 指定返回值类型为 undefined,函数体中必须显示的 return undefined 才可以
const handleTs = (): undefined => {
// 此处,返回的 undefined 是 JS 中的一个值
return undefined
}
let data = handleTs();
console.log(data, 'data');
// 输出
undefined 'data'
有时候我们函数之中的参数不一定都是必须要传的
参数可以传也可以不传 这个时候用到的可选参数
- 可选参数:在可传可不传的参数名称后面添加 ?(问号)
比如我们上面的函数可以更改为:
// 函数可选参数
const handleTs =(numA?:number,numB?:string):string =>{
console.log(numA,numB,'numA,numB');
return numB?numA+numB:numA+'';
}
let total=handleTs(1,'2');
console.log(total,'total');
需要注意的是:可选参数只能出现在参数最后,也就是说可选参数后面不能再出现必选参数
2、对象类型
常见的对象是这样子:
let person= {}
当我们约定为对象:
let person: {} = {}
{} 'person'
console.log(person,'person');
// 添加属性以后
let person: { name: string } = {
name: '同学'
}
console.log(person,'person');
// 输出的结果就是:
{name: '同学'}'person'
如果这个对象之中有方法呢
指定对象的多个属性类型时,使用 `;`(分号)来分隔
那我们的方法就写成了
let person: { name: string; sayHi(): void } = {
name: 'jack',
sayHi() {
console.log('sayHi');
}
}
console.log(person,'person');
优化一下我们上面的函数
通过换行来分隔多个属性类型,可以去掉 `;`
let person: {
name: string
sayHi(): void
} = {
name: 'jack',
sayHi() {console.log('sayHi');}
}
使用类型别名简化对象约束
// 创建类型别名
type Person = {
name: string
sayHi(): void
}
// 使用类型别名作为对象的类型
let person: Person= {
name: 'jack',
sayHi() {console.log('sayHi');return 'sayHi';}
}
console.log(person,'person');
接下来我们要调取上面对象之中的sayHi 方法,这个时候我们要传入一个参数type
// 创建类型别名
type Person = {
name: string
sayHi(type:string,total:string): void
}
// 使用类型别名作为对象的类型
let person: Person= {
name: 'jack',
sayHi(type,total) {
console.log('sayHi');
console.log(type,'type');
console.log(total,'total');
return 'sayHi';
}
}
console.log(person,'person');
把上面的方法改成我们的箭头函数形式
// 创建类型别名
type Person = {
name: string
sayHi:(type:string,total:string)=>void
}
// 使用类型别名作为对象的类型
let person: Person= {
name: 'jack',
sayHi(type,total) {
console.log('sayHi');
console.log(type,'type');
console.log(total,'total');
return 'sayHi';
}
}
console.log(person,'person');
但如果其实total并不需要呢?
使用 ? 来表示,这个时候我们`person`对象上的`sayHi`方法就可以随意使用
type Person = {
name: string
sayHi:(type:string,total?:string)=>void
}
person.sayHi('type');
3、接口类型
当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的
解释:
- 使用 interface 关键字来声明接口
- 接口名称(比如,此处的 IPerson),可以是任意合法的变量名称,推荐以 I 开头 声明接口后,直接使用接口名称作为变量的类型
所以我们上面的person个人类就可以更改为:
// 接口
interface IPerson {
name: string
age?: number
sayHi:(type:string,total?:string)=>void
}
// 使用类型别名作为对象的类型
let person: IPerson= {
name: 'jack',
age:15,
sayHi(type,total) {
console.log('sayHi');
console.log(type,'type');
console.log(total,'total');
return 'sayHi';
}
}
person.sayHi('type');
console.log(person,'person');
到这里我们发现
**interface(接口)**
和 **type(类型别名)**
竟然几乎是差不多的
interface(接口)和 type(类型别名)的对比:
- 相同点:
- 都可以给对象指定类型
- 不同点:
- interface(接口) 只能为对象指定类型
- 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名
推荐:能使用 type 就是用 type
接口继承
如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用
还是我们上面的这个接口
// 接口
interface IPerson {
name: string
age?: number
sayHi:(type:string,total?:string)=>void
}
// 使用类型别名作为对象的类型
let person: IPerson= {
name: 'jack',
age:15,
sayHi(type,total) {
console.log('sayHi');
console.log(type,'type');
console.log(total,'total');
return 'sayHi';
}
}
接下来有几个都要用到这种同样的接口
方式一
interface IPerson1 {
name: string
age?: number
}
interface IPerson2 {
name: string
age?: number
sayHi:(type:string,total?:string)=>void
}
方式二(接口继承)
extends(继承)
使用 extends(继承)关键字实现了接口 IPerson2 继承 IPerson1
// 更好的方式:
interface IPerson1 { name: string;age?: number}
// 继承 IPerson1
interface IPerson2 extends IPerson1 {
sayHi:(type:string,total?:string)=>void
}
// 使用类型别名作为对象的类型
let person: IPerson2= {
name: 'jack',
age:15,
sayHi(type,total) {
console.log('sayHi');
console.log(type,'type');
console.log(total,'total');
return 'sayHi';
}
}
person.sayHi('type');
console.log(person,'person');
4、元组
项目中可能要使用到地图,涵盖经纬度
正常情况下我们的经纬度是这样子的:
let mapsition: number[] = [116.2317, 39.5427]
console.log(mapsition,'mapsition');
但是上面的方式没办法限制我们有几个经纬度,三个肯定错误的
这个时候就用到了元组 Tuple
规定两个数字类型的:
let mapsition:[number, number] = [116.2317, 39.5427]
可以看出,元组类型可以确切地标记出有多少个元素,以及每个元素的类型
5、类型推论
在 TS 中总会有地方不明确指出类型的地方,咋办呢?这个时候,TS 的类型推论机制就会帮助我们提供类型
换句话说:类型注解可以省略不写
- 声明变量并初始化时
- 决定函数返回值时
let age = 18
// 变量 age 的类型被自动推断为:number
当函数未定义的时候
function add(num1: number, num2: number) {
return num1 + num2
}
// 上面的add函数返回值的类型被自动推断为:number
类型推断能帮助我们更高的提升技巧
能省略类型注解的地方就省略,充分利用TS类型推论的能力
鼠标放在变量名称上,利用 VSCode 的提示来查看类型
在 VSCode 中写代码的时候,多看方法、属性的类型,养成写代码看类型的习惯
6、字面量类型
看下面这个字符串的定义,通过上面的类型推论,
let str1 = 'Hello TS'
const str2 = 'Hello TS'
//可以从我们的类型推断中看出:
str1 的类型为:string
str2 的类型为:string
//但显然不是,正确的是
str1 的类型为:string
str2 的类型为:‘Hello TS’
这是什么原因呢:
**解释:**
str1 是一个变量(let),它的值可任意变化为其他字符串,所以类型为:string
str2 是一个常量(const),它的值不能变化只能是 ‘Hello TS’,所以,它的类型为:‘Hello TS’
也就是任意 JS 字面量(比如,对象、数字等)都可以作为类型使用
比如鼠标上下左右移动的游戏之中:
// 使用自定义类型:
type Direction = 'up' | 'down' | 'left' | 'right'
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,会有类型提示:
changeDirection('up')
参数 direction 的值只能是 up/down/left/right 中的任意一个(更精确、严谨)
然后我们下面的结果中可以看到:
在这里我们看到的思想还是一样的:我总结起来就是约束
的思想
7、枚举enum
枚举的功能类似于字面量类型+联合类型组合的功能
// 创建枚举
enum Direction { Up, Down, Left, Right }
// 使用枚举类型
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Right)
也就是说枚举其实是定义一组命名常量,然后界定在其中一个呗。
如果有个初始值,如何在枚举之中给这些值赋值一个初始值呢
// 创建枚举
enum Direction { Up=2, Down=4, Left, Right }
// 使用枚举类型
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Right)
数字枚举和字符串枚举有什么不同呢
字符串枚举必须是有初始值
// 注意:字符串枚举没有自增长行为,因此,字符串枚举的每个成员必须有初始值
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
// 使用枚举类型
function changeDirection(direction: Direction) {
console.log(direction)
}
// 调用函数时,需要应该传入:枚举 Direction 成员的任意一个
// 类似于 JS 中的对象,直接通过 点(.)语法 访问枚举的成员
changeDirection(Direction.Right)
整个演变过程编译 => 被编译为以下 JS 代码:
var Direction;
(function (Direction) {
Direction['Up'] = 'UP'
Direction['Down'] = 'DOWN'
Direction['Left'] = 'LEFT'
Direction['Right'] = 'RIGHT'
})(Direction || Direction = {})
也就是说枚举也是给定的多选一
8、any 类型
原则:不推荐使用 any!这会让 TypeScript 变为 “AnyScript”(失去 TS 类型保护的优势)
当值的类型为 any 时,可以对该值进行任意操作,不会有代码提示
正常情况下
let obj: number = 0;
obj=100;
当我们写错的时候会给予我们提示:
当时更改为any的时候,所有的提示都已经失效了!
let obj: any = 0;
obj='100';
9、 类型断言
语法:使用 as 关键字实现类型断言
有时候我们十分明确一个值的类型
const aLink = document.getElementById('link')
console.log(aLink, 'aLink');
但是上述获取的只包含所有标签公共的属性或方法,不包含 a 标签特有的 href 等属性
类型断言可以为我们指定更加具体的类型
const aLink = document.getElementById('link') as HTMLAnchorElement //类型断言
这里我们可以通过 `__proto__` 属性来获取 DOM 元素的原型对象,然后从原型对象中获取元素的类型。
// 通过 __proto__ 属性获取原型对象
const proto = Object.getPrototypeOf(aLink);
console.log(proto, 'proto');
const elementType = proto.constructor.name;
console.log(elementType, 'elementType-输出元素的类型');
10、typeof
TS 提供了 typeof 操作符:
可以在类型上下文中引用变量或属性的类型(类型查询)
简化
正常情况下我们下一个计算并返回值:
let p = { x: 1, y: 2 }
function getTotal(point: { x: number; y: number }) {
console.log(point.x + point.y, 'getTotal');
}
getTotal(p)
//使用typeof
let p = { x: 1, y: 2 }
function getTotal(point: typeof p) {
console.log(point.x + point.y, 'getTotal');
}
getTotal(p)
我理解为typeof 其实是类型推断和抽离思想的集合
总结:感觉Ts的使用给我们带来了一种类型的约束,当时,对js或者代码的加速之类的其实没起什么作用,更多的是一种辅助开发类型的东西。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)