大家好,我是王天~
这篇文章是 ts入门指南系列中第6 篇,主要讲解ts中的 函数、枚举、别名应用~ 如有不对的地方 欢迎留言反馈哈
函数
在TypeScript中,可选参数和默认参数、剩余参数以及函数重载可以帮助我们更灵活地定义和使用函数。
1、可选参数和默认参数:
可选参数允许我们在调用函数时省略一些参数,而默认参数为函数的参数提供了默认值。
举个例子,假设我们有一个函数greet
用于问候,接受一个名称参数和一个可选的消息参数:
function greet(name: string, message?: string) {
if (message) {
console.log(`Hello, ${name}! ${message}`);
} else {
console.log(`Hello, ${name}!`);
}
}
当我们调用这个函数时,可以只传递名称参数,而可选的消息参数可以省略:
greet("Alice"); // 输出:Hello, Alice!
如果我们想要提供消息参数,可以在调用时传递它:
greet("Bob", "How are you?"); // 输出:Hello, Bob! How are you?
此时,函数内部会打印带有消息的完整问候语。可选参数和默认参数可以使函数使用更加方便和灵活。
2、剩余参数:
剩余参数允许我们在函数中接受不定数量的参数,并将它们作为数组在函数内部使用。
举个例子,我们有一个函数sum
用于计算一组数字的总和:
function sum(...numbers: number[]) {
let total = 0;
for (let number of numbers) {
total += number;
}
return total;
}
我们可以传递任意数量的参数给函数,并且函数内部会将这些参数相加并返回结果:
console.log(sum(1, 2, 3)); // 输出:6
console.log(sum(4, 5, 6, 7, 8)); // 输出:30
剩余参数使用三个点(...
)前缀来表示,它将多个参数收集到一个数组中,方便在函数内部进行处理。这样,我们可以更便捷地传递和处理不定数量的参数。
3、重载:
我们在js中总是不经意间使用函数重载,根据不同的参数类型或个数来执行不同的逻辑,这就是函数重载。
它在类型检查和类型推断方面提供了更强大的功能。
下面例子,展示了如何使用函数重载来实现一个根据参数的不同选择执行不同逻辑的函数:
function formatData(data: string): string;
function formatData(data: number): number;
function formatData(data: string | number): string | number {
if (typeof data === 'string') {
return data.toUpperCase();
} else if (typeof data === 'number') {
return data.toFixed(2);
}
}
const result1 = formatData('hello'); // 返回类型为 string,结果为 "HELLO"
const result2 = formatData(3.14159); // 返回类型为 number,结果为 3.14
上述例子中,我们定义了一个名为 formatData 的函数,它接受一个参数 data,可以是字符串或数字类型。通过函数重载,我们提供了两个函数类型定义:第一个重载接受字符串参数并返回字符串,第二个重载接受数字参数并返回数字。在函数体实现中,根据不同的参数类型执行不同的逻辑操作。
通过使用函数重载,我们可以在类型安全的环境中,根据参数类型提供不同的实现,提高代码的可靠性和可读性。这对于需要根据参数进行不同处理的场景非常有用,比如解析不同类型的输入数据、根据参数类型进行计算等等
枚举
1. 简介
在 TypeScript 中,枚举(Enum)是一种定义常量的方式,可以用简洁、可读性强的方式,定义一组命固定集合的值(常量)。
在 JavaScript 中,在没有枚举语言特性的情况下,常常使用以下方法来表示一组常量:
-
使用命名的变量或常量。
const RED = "red";
const GREEN = "green";
const BLUE = "blue";
这种方式可能容易出错,因为限制了变量的作用域,而且可能会有命名冲突或拼写错误等问题。
-
使用简单的对象字面量。
const Colors = {
RED: "red",
GREEN: "green",
BLUE: "blue"
};
使用对象字面量可以提供一些命名空间的支持,但并不能限制变量值的修改,这意味着你仍然可以通过赋值操作改变枚举值。
这就是 TypeScript 中枚举的作用所在:它提供了一种更强大、更安全的方式来定义一组有限的常量。
枚举在 TypeScript 中的优势和解决了 JavaScript 中的一些痛点包括:
-
类型安全性:枚举在 TypeScript 中是具有类型的,编译器可以在编译时进行类型检查,确保只使用枚举中定义的值。
-
命名空间和作用域:枚举在一定程度上提供了命名空间的支持,可以更好地组织和管理常量集合,并且不会发生命名冲突。
-
可读性:通过使用枚举,可以直观地了解代码中某个值的含义,提高了代码的可读性和可维护性。
-
自文档化:枚举可以作为文档的一部分,提供了一个可供其他开发人员查阅的可扩展的集合。
总之,TypeScript 中的枚举提供了一种更加结构化和类型安全的方式,来表示固定集合的值,并解决了 JavaScript 中使用常量时的一些问题。
2. 基本用法
枚举的基本用法是定义一个枚举类型,然后使用它来声明变量或函数参数。下面是一个基本的示例:
// 定义一个表示颜色的枚举
enum Color {
Red,
Green,
Blue
}
// 使用枚举类型声明变量
let myColor: Color = Color.Green;
console.log(myColor); // 输出: 1
在这个例子中,我们定义了一个枚举类型 Color
,它包含了三个成员:Red
、Green
和 Blue
。
默认情况下,枚举成员的值从 0
开始自动递增,所以 Red
的值是 0
,Green
的值是 1
,Blue
的值是 2
。
我们可以使用枚举类型来声明变量 myColor
,并将它的值设置为 Color.Green
。最后,我们打印了 myColor
的值,结果是 1
。
3. 常数项和计算所得项
在枚举中,成员可以是常数项或计算所得项。
常数项是声明枚举时就已经指定好的值,这些值不能被修改。如果没有给常数项赋值,它们将按照从 0
开始的顺序递增。
计算所得项是根据表达式在运行时计算出的值,可以用于根据之前定义的常数项进行计算。计算所得项必须在表达式中使用,不能直接赋值给其他成员。
以下是一个示例,演示了常数项和计算所得项的用法:
enum Direction {
Up, // 常数项,默认值为 0
Down, // 常数项,默认值为 1
Left = 10, // 常数项指定具体值为 10
Right = Left + 1 // 计算所得项,值为 11(Left 的值加 1)
}
console.log(Direction.Up); // 输出: 0
console.log(Direction.Left); // 输出: 10
console.log(Direction.Right); // 输出: 11
这个例子中,Direction
枚举包含了四个成员。
Up
和 Down
是常数项,默认情况下它们的值分别是 0
和 1
。Left
是一个常数项,我们明确地将它的值设置为 10
。Right
是一个计算所得项,它的值是 Left + 1
,即 10 + 1
,结果是 11
。
4. 常数枚举
常数枚举是一种特殊类型的枚举,在编译时会被删除,并且对枚举成员的引用会被替换为具体的值。常数枚举可以在一些性能优化和减少代码量的情况下使用。
下面是一个示例,展示了常数枚举的用法:
const enum Size {
Small,
Medium,
Large
}
function printSize(size: Size): void {
console.log(size);
}
printSize(Size.Small); // 编译后会直接替换为具体的值
在这个例子中,我们使用 const enum
关键字定义了一个常数枚举 Size
,它包含三个成员。
printSize
函数接受一个参数 size
,类型为 Size
枚举。在函数体内,我们打印了 size
的值。由于 Size
是一个常数枚举,在编译时会被直接替换为具体的值。
所以,当我们调用 printSize(Size.Small)
时,编译后的代码将直接打印 0
。
5. 实战开发案例
让我们以一个实战开发案例来综合应用学到的 TypeScript 枚举知识。
假设你正在开发一个游戏,游戏中有不同种类的角色,每个角色都有不同的属性。我们可以使用枚举来表示不同种类的角色以及它们的属性。
以下是一个示例代码:
enum MonsterType {
Goblin,
Orc,
Troll
}
interface Monster {
type: MonsterType;
name: string;
health: number;
attack: number;
}
function createMonster(monsterType: MonsterType, name: string): Monster {
let monster: Monster;
switch (monsterType) {
case MonsterType.Goblin:
monster = { type: monsterType, name, health: 10, attack: 5 };
break;
case MonsterType.Orc:
monster = { type: monsterType, name, health: 20, attack: 10 };
break;
case MonsterType.Troll:
monster = { type: monsterType, name, health: 30, attack: 15 };
break;
default:
throw new Error("未知的角色类型");
}
return monster;
}
const goblin = createMonster(MonsterType.Goblin, "格鲁皮");
console.log(goblin);
在这个案例中,我们定义了一个枚举类型 MonsterType
来表示不同种类的角色。
别名
在 TypeScript 中,type
关键字用于创建类型别名(Type Aliases)。类型别名允许你为一个具体的类型或类型组合定义一个名称,以便在代码中重复使用。
type
的作用有以下几个方面:
-
类型复用: 通过类型别名,你可以将一个复杂的类型定义为一个名称,然后在需要使用该类型的地方直接使用该名称。这有助于提高代码的可读性和可维护性。
type User = {
name: string;
age: number;
};
type Callback = (data: User) => void;
function fetchData(callback: Callback) {
// ...
}
在上面的例子中,通过类型别名 User
和 Callback
分别定义了一个用户对象类型和一个回调函数类型,然后在 fetchData
函数中使用了这两个类型别名。
-
类型组合: 类型别名还可以用于组合现有的类型来创建新的类型。这可以通过交叉类型(Intersection Types)和联合类型(Union Types)来实现。
type Point = {
x: number;
y: number;
};
type Color = 'red' | 'green' | 'blue';
type ColoredPoint = Point & { color: Color };
type Shape = Square | Circle;
interface Square {
kind: 'square';
size: number;
}
interface Circle {
kind: 'circle';
radius: number;
}
在上述代码中,ColoredPoint
类型通过交叉类型将 Point
类型和包含 color
属性的对象类型组合而成,Shape
类型通过联合类型将 Square
和 Circle
接口组合而成。
-
类型推导(Type Inference): 当你使用类型别名初始化变量时,TypeScript 可以推导变量的类型,并将其视为该类型别名所代表的类型。
type Point = {
x: number;
y: number;
};
const origin: Point = { x: 0, y: 0 };
在上面的例子中,通过类型别名 Point
定义了一个坐标点类型,然后通过赋值给 origin
变量,TypeScript 推导出 origin
的类型为 Point
。
类型别名的使用使得 TypeScript 中的类型定义更加灵活和可维护。通过使用类型别名,你可以更好地组织和管理代码中的复杂类型,提高代码的可读性和可维护性。
此外,类型别名还可以与其他 TypeScript 的高级类型特性(如泛型、条件类型等)结合使用,进一步增强类型系统的能力。
type vs interface
表示类型
类型别名(type)
是一个定义别名的工具,可以将多个类型组合起来形成一个新类型。比如特定的对象结构,联合类型、函数类型等、可以表示非对象类型
可以应用于以下数据类型:
-
基本类型(如字符串、数字、布尔值等)
-
联合类型(Union Types)
-
交叉类型(Intersection Types)
-
元组(Tuple)
-
函数类型(Function Types)
-
对象类型(Object Types)
-
类型字面量(Type Literals)
-
类型别名的自身引用(Recursive Type Aliases)
接口(interface)
主要用于定义对象的形状和结构,只能表示对象类型
-
对象类型(Object Types)
-
类类型(Class Types)
继承状态
-
type 不可继承
-
interface可以继承 class 、interface、type
如果喜欢或者 有所启发,欢迎 star,对作者也是一种鼓励。