React-TypeScript进阶使用

举报
林太白 发表于 2025/01/31 23:32:02 2025/01/31
184 0 0
【摘要】 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

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。