目录
2)unknown类型相对来说更安全,它表示一个值的类型是未知的,但操作之前会进行类型断言或类型检查
一、基本
1. any vs unknown
1)any允许对值进行任意操作,不会进行任何类型检查
可以将一个any类型的变量赋值为任何类型的值,也可以调用它任何可能的方法,即使这个方法在实际类型下并不存在,编译器都不会给出任何类型错误提示。
let value: any;
value = 10;
value = "string";
value.toUpperCase(); // 这里虽然 value 可能不是字符串,但调用 toUpperCase 方法也不会报错,存在类型安全隐患
let num: number;
let anyValue: any;
anyValue = 5;
num = anyValue; // 可以将 any 类型的值赋值给 number 类型的变量,不会报错,即便这样可能存在类型不匹配风险
2)unknown类型相对来说更安全,它表示一个值的类型是未知的,但操作之前会进行类型断言或类型检查
let value: unknown;
value = 10;
if (typeof value === "number") {
console.log(value * 2); // 通过 typeof 进行类型检查后,确定是数字类型再进行操作,符合类型安全要求
} else {
console.log("不是数字类型");
}
let value: unknown;
let num: number;
value = 10;
num = value as number; // 需要通过类型断言将 unknown 类型的值转换为 number 类型才能赋值给 num,否则会报错
3)应用场景
(1)any
通常建议在一些特定的、临时性的场景下使用,比如在将JavaScript代码快速迁移到TypeScript项目的初期,对于那些暂时还不清楚具体类型,且需要进行灵活处理的代码部分,可以先用any类型标记,但后续应减少any类型在代码中的使用。
(2)unknown
更适用于在编写代码时无法提前确定类型,但又希望在后续使用过程中通过合理的类型检查和断言来保证类型安全的情况。
2. Record vs Object
1)Object类型没有对具体的对象结构进行限定
在使用过程中很难准确把握其具体的数据结构,容易出现类型错误。
let myObject: Object;
myObject = { name: 'John' };
myObject = [1, 2, 3]; // 数组也可以赋值给它,因为数组也是对象
myObject = function() {}; // 函数同样可以赋值给它
2)Record精准定义对象结构
要求明确指定对象属性名的类型以及对应属性值的类型。
用于创建一个对象类型,其中 K 表示对象属性的键(key)的类型,T 表示对应属性值的类型。
type UserRoles = 'admin' | 'user' | 'guest';
type UserPermissions = Record<UserRoles, boolean>;
const permissions: UserPermissions = {
admin: true,
user: false,
guest: false
};
3. type vs interface
简言之:
interface:定义某种规范(规范性、可扩展性)
(接口用于定义对象的结构类型,它描述了一个对象应该具有哪些属性以及这些属性的类型,但它本身并不会实现具体的逻辑,只是一种类型约束的规范。)
type:定义复杂类型(灵活性、多样化)
(类型别名就是给一个类型定义一个新的名字,通常用于复杂的类型,方便在后续代码中引用,使得代码更加简洁明了,易于理解和维护。)
如果是在定义对象的结构形状、类的实现规范或作为公共的类型契约等场景,优先考虑使用interface,如果是需要定义联合类型、交叉类型、函数类型等复杂类型结构,或对已有的类型进行复用、重命名,以及一些需要精确的类型控制时,选择type类型别名。
1)interface
interface Person {
name: string;
age: number;
sayHello(): void;
}
interface Shape {
area(): number;
}
class Circle implements Shape {
radius: number;
constructor(radius: number) {
this.radius = radius;
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
2)type
type UnionType = string | number;
type FunctionType = (param: string) => void;
type IntersectionType = { name: string } & { age: number };
其中:
UnionType 定义了一个联合类型,表示这个类型的值可以是字符串或者数字;
FunctionType 清晰地定义了一个函数类型,接收一个字符串类型的参数且无返回值;
IntersectionType 通过交叉类型定义了一个同时具备 name(字符串类型)属性和 age(数字类型)属性的对象类型。
4. 类型断言
1)“尖括号”语法
let value: any = "hello";
let length: number = (<string>value).length;
但是在.tsx文件中,尖括号会与JSX标签语法冲突。
2)“as”关键字语法
let value: any = "hello";
let length: number = (value as string).length;
对value进行类型断言,让编辑器把它认定为string类型以访问length属性。
interface ApiResponse {
status: string;
data: any;
}
async function fetchData(): Promise<ApiResponse> {
const response = await fetch('https://example.com/api');
return response.json() as ApiResponse;
}
async function processData() {
const result = await fetchData();
if (result.status === "success") {
const userData = result.data as UserData;
console.log(userData.username);
}
}
5. 泛型
在定义函数、类、接口等时不预先指定具体的类型,而是在使用这些函数、类、接口的时候再确定类型,这样可以创建出更加通用、可复用的代码组件。
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
<T>是定义的泛型参数,T可以代表任意类型,在函数参数arr的定义中,表明arr是一个元素类型为T的数组,函数的返回值类型也是T,具体T是什么类型,要在调用函数时根据传入的实际参数来确定。
6. 枚举
枚举是一种特殊的数据类型,允许定义一组命名的常量,能限制取值范围在定义的枚举成员中,
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
默认情况下,Monday的值为0,Tuesday的值为1,以此类推。
使用:
let today: Weekday = Weekday.Monday;
实际应用中常用于表示一组有限的、有关的取值情况。比如处理状态码、方向(如上下左右)、颜色等具有固定几种可选值的场景下,使用枚举更加直观。
enum HttpStatus {
OK = 200,
Created = 201,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404
}
function handleResponse(status: HttpStatus) {
switch (status) {
case HttpStatus.OK:
console.log("请求成功");
break;
case HttpStatus.BadRequest:
console.log("请求参数错误");
break;
// 其他状态码的处理逻辑
}
}
或者区分按钮点击情况:
export enum EditMode {
//新增
create = 'create',
//编辑
edit = 'edit',
//复制
copy = 'copy',
//详情
detail = 'detail'
}