首页 前端知识 TypeScript(五)类型别名及类型符号

TypeScript(五)类型别名及类型符号

2024-09-18 01:09:00 前端知识 前端哥 711 238 我要收藏

目录

引言

类型别名

基本用法 

字面量类型

数字字面量

字符串字面量

布尔字面量

空字面量

枚举字面量

类型符号

联合类型

交叉类型

类型断言

尖括号

as关键字

非空断言

类型保护

typeof

instanceof

类型谓词

索引类型

映射类型

type or interface?

二者的区别:

应用场景

总结

参考文章


引言

本文收录于TypeScript知识总结系列文章,欢迎指正! 

在编写JS代码时,我们通常使用const、var、let来定义一个变量,进行变量的运算或者逻辑编写等。在TS中也有一种类似变量的写法那就是类型别名,与运算逻辑相对应的便是类型符号

类型别名

在前面的文章中,我们介绍了TypeScript中常用的类型,然而在实际项目开发中,不可避免的会遇到各种复杂类型,有些可以使用接口或对象类型来描述,但是有些数据类型更灵活、复杂,那么此时,类型别名是一种非常有用的工具,它可以帮助我们简化代码,提高代码的可读性和可维护性;

基本用法 

类型别名使用 type 类型名 = 类型值 的方式来定义,如

interface IFn {
    (): void
}
interface IDog {
    name: string
    age?: number
    [key: string]: unknown
}
type myStr = string// 字符串
type myNum = number// 数字
type myArr = string[]// 数组
type myFn = IFn// 函数
type myDog = IDog// 接口
type myObj = {
    str: string
    num: number
    arr: myArr
}// 对象
const str: myStr = "string"
const num: myNum = 10
const arr: myArr = ['a', 'b']
const fn: myFn = () => null
const dog: myDog = {
    name: "阿黄"
}
const obj: myObj = {
    str: "string",
    num: 11,
    arr: arr
}

字面量类型

字面量类型(Literal Types)用来表示具体的字面量值,包括字符串、数字、布尔值等。它们可以作为类型注解的一部分,用来限制变量、函数参数、函数返回值等的取值范围

数字字面量

数字字面量类型(Numeric Literal Types)是一种用来表示具体数字值的类型,它使用一个具体的数字来定义一个类型

type Num = 10
const num: Num = 10
const num2: Num = 20 // 抛错,不能将20赋值给类型10

字符串字面量

字符串字面量类型(String Literal Types):用来表示一个具体的字符串值的类型。

type Str = "a"
const num: Str = "a"

布尔字面量

布尔字面量类型(Boolean Literal Types):用来表示一个具体的布尔值的类型。

type Bool = false
const bool: Bool = false

空字面量

空字面量类型(Empty Literal Types):用来表示一个空值的类型,被定义的类型只能被赋值为undefinednull

type Void = void;
const isNull: Void = null
const isUndefined: Void = undefined
const isVoid: Void = void 0

枚举字面量

枚举字面量类型(Enum Literal Types):用来表示一个具体的枚举值的类型

enum Color {
    Red = 1,
    Green,
    Blue
}
type colorBlue = Color.Blue
const blue: colorBlue = 3

类型符号

如果说类型别名是一个人,那么类型符号就是它的灵魂。在TypeScript中,类型符号是构建类型系统的基础,是实现类型别名的核心。

联合类型

联合类型(Union Types)用来表示一个变量可以包含多种类型之一的情况。联合类型使用或符号 |  来连接两个或多个类型,例如:

let strOrNum: string | number = 10
strOrNum = "a"

我们使用上面讲到的类型别名试试

type strOrNum = string | number
const str: strOrNum = "a"
const num: strOrNum = 10

交叉类型

交叉类型(Intersection Types)可以用来将多个类型合并为一个类型。交叉类型使用且符号&进行连接两个或多个类型;值得注意的是交叉类型一般使用于对象定义这种复杂的数据类型,如果使用交叉类型定义基础类型,则会转换为never类型,因为一个类型不可能同时兼备两种基础类型,如

type str = string
type num = number
type StrAndNum = str & num // never类型

上面代码中number和string是互斥的类型,不存在既是number又是string的值,然而程序不会逻辑报错,因为number和string会包装成对象类型

下面是一个交叉类型的案例

type Animal = {
    name: string
    age?: number
}
type Dog = {
    readonly color: string
    getColor(): string
}

type WhiteDog = Animal & Dog

const whiteDog: WhiteDog = {
    name: "阿黄",
    color: "white",
    getColor() {
        return this.color
    },
}

tips:交叉类型和接口继承有点类似,如果对象同时存在同名不同类型的属性时会抛错 

类型断言

类型断言(Type Assertion)是一种显式地告诉编译器变量的类型的方式,它允许我们手动指定一个变量的类型,从而绕过TypeScript编译器对该变量类型的检查。

断言的使用方式有两种,分别是尖括号和as关键字

尖括号

let str: unknown
const len: number = (<string>str).length

在上面的代码中,我们使用断言的方式将str转换为字符串类型,并获取字符串的长度

as关键字

let str: unknown
const len: number = (str as string).length

与尖括号一样,在变量的后面增加 as 类型 使用类型断言

非空断言

在JS中,我们通常使用可选链操作符( .? )来判断一个对象是否为空,来增加代码健壮性

在TS中,我们可以使用非空断言(Non-null Assertion)来告诉编译器变量或表达式一定不为空

在下面代码中,str可能是null或者字符串类型,若直接使用str.length会提示:对象可能为"null"

const str: string | null = null
const len: number = str.length

tips:上述代码没抛错可能因为在tsconfig中未开启strictNullChecks

此时我们可以通过 ! 非空断言或 ? 可选链操作符以及 ?? 空值合并操作符告诉编译器str不为null或处理变量为空的异常

let str: string | null = null
if (window) {
    str = "abc"
}
const len: number = str!.length
const len2: number = str?.length ?? 10

类型保护

typeof

TypeScript的typeof和JavaScript的typeof在语法上是相同的,但是在行为上有区别。在TS中typeof可以获取变量的类型,如

let num = 10
let str = 'abc'
type myStr = typeof str
type myNum = typeof num
const str1: myStr = "bcd"
const num1: myNum = 20

instanceof

instanceof与JS中的使用方式相同,animal instanceof Animal 表示animal是不是Animal的实例化对象,这里就不多赘述

class Animal { }
const animal = new Animal()
console.log(animal instanceof Animal);

类型谓词

类型谓语在函数运行时检查返回值的类型,并且进行类型保护,它的作用是缩小类型范围。类型谓语使用is表示,下面是一个简单的例子

interface IAnimal {
    name: string
    age: number
}
function isAnimal(animal): animal is IAnimal {
    return "name" in animal
}
const animal: any = {
    name: '阿黄',
    age: 10
};

if (isAnimal(animal)) {
    console.log(animal.name)
}

可以看到,我们定义了一个IAnimal接口,其中有两个属性,在isAnimal函数中我们把函数返回值写成类型谓语的形式,表示返回值的animal就是接口IAnimal的实现,此时在下方取animal.name时是正常的。

此时我们重新定义一个接口IDog

interface IDog {
    color: string
    hobby: string
}

将上面的isAnimal函数改成这样,此时再执行isAnimal函数后,再去访问name属性就会抛错

function isAnimal(animal): animal is IDog {
    return "name" in animal
}
if (isAnimal(animal)) {
    console.log(animal.name) //报错,类型“IDog”上不存在属性“name”
}

除此之外我们同样可以结合交叉类型,联合类型等灵活的使用is

索引类型

在TS中我们可以使用keyof关键字来获取一个对象类型或接口的类型的所有键的联合类型。

interface IDog {
    name: string
    color: string
    age: number
}
type IDogKeys = keyof IDog // 相当于 'name' | 'color' | 'age'
const dogAge: IDogKeys = "age"
const dogColor: IDogKeys = "color"
const dogName: IDogKeys = "name"

上述代码的IDogKeys相当于获取了IDog的所有键的联合类型,使用类型别名描述对象的方式同样可以达到效果

type IDog = {
    name: string
    color: string
    age: number
}

type IDogKeys = keyof IDog // 相当于 'name' | 'color' | 'age'
const dogAge: IDogKeys = "age"
const dogColor: IDogKeys = "color"
const dogName: IDogKeys = "name"

映射类型

in运算符在TypeScript和JavaScript中都存在,并且用法基本相同,用来检查某个属性是否存在于某个对象或其原型链中

interface IDog {
    name: string
    color?: string
}
const dog: IDog = {
    name: "阿黄"
}
console.log('name' in dog);// true
console.log('color' in dog);// false

使用索引类型加映射类型限制索引区间

interface IDog {
    name: string
    color?: string
}
type ISmallDog = {
    [key in keyof IDog]: string// 这行表示只能取IDog的key
}
const dog: ISmallDog = {
    name: "阿黄",//此时的type需要与ISmallDog的相同
    hobby: 'ball'// 报错,IDog找不到hobby
}

type or interface?

Type和Interface都是用于定义类型的关键字,那么其二者分别适用什么场景?

二者的区别:

  • Type可以声明基本类型、联合类型、元组、枚举、函数等,而Interface主要用于定义对象、类、函数等复杂类型
  • Type是类型别名的作用,不能被扩展,而Interface可以被继承
  • Type可以使用联合类型,交叉类型等高级类型进行操作,而Interface不支持

应用场景

Type:定义一个基本类型、联合类型、元组、枚举等类型,或需要使用高级类型操作的类型

Interface:定义对象、类等复杂类型,或使用对类型扩展,继承等操作时接口更适用

tips:使用type定义的对象类型同样可以被类实现

type IAnimel = {
    name: string
    getName(): string
}
class Animal implements IAnimel {
    name = 'dog'
    getName() {
        return this.name
    }
}

总结

本文从类型别名和类型符号两个模块;介绍了类型别名的使用及字面量类型,并且从联合类型,交叉类型等高级类型操作的方面将类型别名的应用场景连接起来,最后将类型别名对比接口介绍了二者的使用场景

感谢你看到了最后,有任何问题可以在评论区讨论,如果文章对你有帮助,还望多多支持一下!

参考文章

高级类型 - TypeScript 中文手册

转载请注明出处或者链接地址:https://www.qianduange.cn//article/18364.html
标签
评论
会员中心 联系我 留言建议 回顶部
复制成功!