当前位置: 首页 > news >正文

4. TypeScript 类型推断与类型组合

一、类型推断

(一) 什么是类型推断

TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。

  • 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。
  • 通过分析上下文和初始值,TypeScript 能确保变量和函数在整个代码中以一致且预期的类型进行操作。
let age = 25;
let name = "John";console.log(`Age: ${age}`);
console.log(`Name: ${name}`);

在这个示例中,

  • TypeScript 根据赋值自动推断 age 为数字类型,name 为字符串类型。
  • 这种自动检测机制在无需显式类型注解的情况下,确保了类型安全。

(二) 变量类型的推断

let x = 10; // TypeScript 推断 x 为 number 类型
console.log(typeof x);

在这个示例中,

  • TypeScript 根据初始值 10 推断变量 x 的类型为 number。
  • 这确保了 x 只能保存数值类型的值,从而提高了类型安全性。

输出:

number

(三) 数组类型推断

let fruits = ["Apple", "Banana", "Cherry"]; // TypeScript 推断 fruits 为 string[]
console.log(fruits);

在这个例子中,

  • TypeScript 根据初始值推断出 fruits 的类型为字符串数组(string[])。
  • 这可以防止添加其他类型的元素,保持数组的一致性。

输出:

[ 'Apple', 'Banana', 'Cherry' ]

(四) 函数返回类型的推断

function add(a: number, b: number) {return a + b; // TypeScript 推断返回类型为 number
}
console.log(add(5, 10));

在这个例子中,

  • add 函数的返回类型被推断为 number,因为它返回两个数字的和。
  • 这确保了该函数始终返回数值类型,避免了类型相关的错误。

输出:

15

二、类型组合

在 TypeScript 中,类型组合使你能够通过将多种类型合并为单一类型,创建灵活且可复用的组件。这种方式可以更精确地控制数据的结构,特别适用于处理复杂的数据结构或在类型安全的前提下处理不同的情况。

(一) 联合类型

TypeScript 联合类型 具有将一种或多种不同类型的数据(例如 numberstringfloatdouble 等)组合在一起的能力。它是表达变量可以拥有多种类型的最强大方式。使用 管道符号('|') 来组合两个或多个数据类型,从而实现联合类型。语法如下:

(type1|type2|type3|...|type-n)

1. 一般用法

例1:

let value: number | string;  
value = 190;  
console.log("Numeric value of the value: " + value);  
value = "Welcome to TypeScript!";  
console.log("String value of the value: " + value);

将上述代码编译后,会生成以下的 JavaScript 代码。

"use strict";
let value;
value = 190;
console.log("Numeric value of the value: " + value);
value = "Welcome to TypeScript!";
console.log("String value of the value: " + value);

输出:

190
Welcome to TypeScript!

例2:

在这个例子中,变量 geeks 是联合类型,用 (string | number) 表示。因此,我们可以给它赋值为字符串或数字,除此之外的类型都不允许。

let geeks: (string | number);
geeks = 123;   // 可以,赋值为数字
geeks = "XYZ"; // 可以,赋值为字符串
geeks = true;  // 编译错误,不允许赋值为布尔类型

2. 函数参数作为联合类型

我们可以将联合类型用作函数参数。在这个例子中,参数 geeks 是联合类型,你可以传入字符串或者数字类型的值,否则编译器会报错。示例如下:

function displayType(geeks: (string | number)) {if(typeof(geeks) === "number")console.log('geeks 是数字。')else if(typeof(geeks) === "string")console.log('geeks 是字符串。')
}// 输出:geeks 是数字。
displayType(49); // 输出:geeks 是字符串。
displayType("GFG"); // 编译错误:类型 'true' 的参数不能赋给类型为 string | number 的参数
displayType(true);  

3. 数组作为联合类型

在联合类型中,我们也可以传递数组。程序声明了一个数组,该数组可以表示数字集合或字符串集合。示例:

// 由 TypeScript 1.8.10 生成
var arr = [2, 5, 7, 5, 11, 15];console.log("Display the array elements");// 循环输出数组元素
for (var i = 0; i < arr.length; i++) {console.log(arr[i]);
}// 重新赋值为字符串数组
arr = ["A", "G4G", "GFG", "GeeksforGeeks"];console.log("Display the array elements");// 循环输出数组元素
for (var i = 0; i < arr.length; i++) {console.log(arr[i]);
}

输出:

Display the array elements
2
5
7
5
11
15
Display the array elements
Geeks
G4G
GFG
GeeksforGeeks

联合类型可以替代枚举(enums):枚举是一组常量类型的列表,默认情况下,枚举的索引值是(0、1、2、3 等)。枚举实际上是被编译转换的(将一种语言编写的源代码转换成另一种具有相似抽象级别的语言),最终生成类似 JavaScript 的代码。

(二) 类型别名

在 TypeScript 中,类型别名允许你为已存在的类型指定一个自定义名称,从而提升代码的可读性和复用性。

  • 为复杂类型(如联合类型或对象类型)提供简写形式。
  • 允许为基本类型、对象类型或函数类型命名,使代码更加清晰。
  • 简化重复的类型定义,增强代码的可维护性。
type Point = {x: number;y: number;
};type Shape = "circle" | "square" | "rectangle";function drawShape(shape: Shape, position: Point): void {console.log(`Drawing a ${shape} at (${position.x}, ${position.y})`);
}drawShape("circle", { x: 10, y: 20 });
  • Point 是一个类型别名,表示具有 x 和 y 两个 number 类型属性的对象。
  • Shape 是一个由特定字符串字面量组成的联合类型别名。
  • drawShape 函数接受一个 Shape 和一个 Point 参数,确保了强类型安全性和代码的清晰性。

输出:

Drawing a circle at (10, 20)

1. 类型别名的参数:

AliasName(别名名称)
这是你为类型别名指定的名称,必须是有效的 TypeScript 标识符。
示例:PointShapeUserProfile

ExistingType(已有类型)
指的是该别名所代表的实际数据类型或结构。
示例:stringnumber{ x: number; y: number; }

2. 联合类型的别名

type ID = number | string;let userId: ID;
userId = 101;       // 有效的赋值
userId = "A123";    // 也是有效的赋值
  • ID 是一个类型别名,允许变量的类型为数字或字符串。
  • 这为 userId 提供了灵活性,使其既可以接受数字类型也可以接受字母数字混合的标识符。

输出:

Origin: { x: 0, y: 0 }
Distance from Origin: 0

3. 使用类型别名定义用户资料

type UserProfile = {username: string;email: string;age: number;
};const user: UserProfile = {username: "Felixlu",email: "lurongtao@pku.org.cn",age: 24,
};function greetUser(profile: UserProfile): string {return `Hello, ${profile.username}! You are ${profile.age} years old. Your email is ${profile.email}.`;
}console.log(greetUser(user));
  • UserProfile 是一个对象类型别名,包含 username、email 和 age 属性。
  • greetUser 函数使用该别名,确保接收的参数是结构正确的用户资料。

输出:

Hello, Felixlu! 
You are 24 years old. 
Your email is lurongtao@pku.org.cn.

4. 使用类型别名定义联合类型

type ID = number | string;function displayId(id: ID): void {console.log(`The ID is ${id}`);
}displayId(101);
displayId("A102");
  • ID 是一个类型别名,表示 number 和 string 的联合类型。
  • displayId 函数接受一个 ID 类型的参数,允许传入多种类型的值,增强了灵活性。

输出:

The ID is 101
The ID is A102

5. TypeScript 类型别名的最佳实践

  1. 使用描述性名称:选择清晰且有意义的类型别名名称,以提升代码的可读性。
  2. 保持类型聚焦:为特定且定义明确的结构定义类型别名,以保持代码清晰。
  3. 记录复杂类型:对复杂的类型别名添加注释或文档,帮助理解类型的具体含义。

(三) Keyof 类型

TypeScript 的 keyof 操作符用于获取某个对象类型中所有键名的联合类型。当你希望以类型安全的方式操作对象的属性名,确保只使用有效键时,它非常有用。

我们可以使用 keyof 来定义适用于任意对象类型的泛型函数,无需预先知道该类型的具体键名。它也可以用来创建接口的只读版本,或者从接口中提取特定的键。

1. 语法

type KeysOfType = keyof ObjectType;

2. 如何使用 keyof 类型操作符?

我们定义了一个包含三个不同属性的接口 Personnameagegender。然后我们定义了一个类型 PersonKeys,它等于 keyof Person,即一个 "name" | "age" | "gender" 的联合类型。

interface Person {name: string;age: number;gender: string;
}
type PersonKeys = keyof Person;

3. 展示 keyof 类型操作符的示例

让我们来看一些 TypeScript 中 keyof 类型操作符的示例。这些示例将帮助你理解 keyof 类型操作符的工作原理。

(1)访问对象属性

在此示例中,我们定义了一个包含三个属性(name、age 和 gender)的接口 Person。我们还定义了一个类型为 Person 的变量 person 并赋予一些值。

interface Person {name: string;age: number;gender: string;
}const person: Person = {name: "John",age: 25,gender: "male",
};function getProperty<T, K extends keyof T>(obj: T, key: K) {return obj[key];
}console.log(getProperty(person, "name")); // "John"
console.log(getProperty(person, "age")); // 25
console.log(getProperty(person, "gender")); // "male"

输出:

John
25
male

代码解释:

  • getProperty 接受一个类型为 T 的对象 obj 和一个键 K
  • 其中 K 是 T 的有效键(K extends keyof T),确保该键存在于对象中。
  • 该函数返回 obj[key] 对应的值。

(2)使用映射类型

在这个示例中,我们定义了一个包含三个属性 name、age 和 gender 的接口 Person。

interface Person {name: string;age: number;gender: string;
}type ReadonlyPerson = {readonly [K in keyof Person]: Person[K];
}const person: ReadonlyPerson = {name: "John",age: 25,gender: "male",
};console.log(person.name); // "John"
console.log(person.age); // 25
console.log(person.gender); // "male"

输出:

John
25
male

代码解释:

  • [K in keyof Person] 创建了键为 K、值类型为 Person[K] 的属性,但这些属性是只读的。
  • 类型 ReadonlyPerson 拥有从 Person 派生的不可变属性。
  • ReadonlyPerson 变量不能修改其属性。
http://www.xdnf.cn/news/929827.html

相关文章:

  • 分析 java 的 Map<String,Map<String, List<Map<String,Integer>>>>
  • Go语言并发模型与模式:Worker Pool 模式
  • 详解鸿蒙Next仓颉开发语言中的动画
  • 勒让德多项式
  • 投屏技术深度解析:从原理到成功率优化实战·优雅草卓伊凡
  • 高级数据结构与算法期末考试速成记录2
  • exec进程替换函数族
  • AOSP CachedAppOptimizer中的冻结和内存压缩功能
  • 11.无重复字符的最长子串
  • LUFFY(路飞): 使用DeepSeek指导Qwen强化学习
  • 34 C 语言字符串转数值函数详解:strtol、strtoll、strtoul、strtoull(含 errno 处理、ERANGE 错误)
  • 创建一个纯直线组成的字体库
  • 【强连通分量 缩点 最长路 拓扑排序】P2656 采蘑菇|普及+
  • Linux 文本三剑客(grep, awk, sed)
  • 运维_集运维核心学习
  • xctf-weak_auth(弱口令)
  • 【C++ 真题】P1747 好奇怪的游戏
  • 23、字节对齐
  • 22、模板特例化
  • WPF 播放器(AudioPlayer 2025)
  • triton学习笔记6: Fused Attention
  • CAN转PROFINET网关设备基本功能介绍
  • Android资源ID冲突解决方案
  • 28、元组的遍历
  • Redis :String类型
  • 第23讲、Odoo18 邮件系统整体架构
  • AIGC行业发展演进:从技术萌芽到智能革命
  • 全面解析:tzst 归档格式的先进性与跨平台文件管理指南
  • RTOS学习之重难点
  • go语言学习 第8章:切片