Typescript高级: 深入实践Record类型

概述

  • Record 类型是TS中其众多强大特性之一
  • 它为我们提供了创建键值对映射的强大能力
  • 极大地增强了代码的灵活性与类型安全性

应用示例


1 )用于配置场景

  • 在复杂的项目中,配置文件往往包含多个模块的不同设置
  • 使用 Record 可以确保配置的键名正确且值类型统一

示例

interface AppConfig {
  port: number;
  env: 'development' | 'production';
}

const config: Record<string, AppConfig> = {
  server: { port: 3000, env: 'development' },
  client: { port: 8080, env: 'production' },
};

2 )枚举场景

  • Record 也常用于将枚举类型转换为易于使用的键值对映射,便于在代码中引用

示例

enum Color {
  Red,
  Green,
  Blue,
}

const colorNameMap: Record<Color, string> = {
  [Color.Red]: 'Red',
  [Color.Green]: 'Green',
  [Color.Blue]: 'Blue',
};

3 )联合类型场景

示例

type Scores = Record<'Math' | 'English' | 'Science', number>;  
  
const studentScores: Scores = {  
    Math: 90,  
    English: 85,  
    Science: 92  
};

4 )表单项的配置

  • 在构建动态表单时,Record 可以用来定义表单项的配置,每个表单项都有其特有的属性

示例

interface FormField {
  type: 'text' | 'checkbox' | 'select';
  label: string;
  required?: boolean;
}

const formConfig: Record<string, FormField> = {
  username: { type: 'text', label: 'Username', required: true },
  agreeTerms: { type: 'checkbox', label: 'Agree to terms', required: false },
};

5 ) 映射类型类似Record

type Invert<T> = {  
  [P in keyof T as T[P] extends string ? T[P] : never]: P;  
};  
  
type Original = { a: '1'; b: '2' };  
type Inverted = Invert<Original>;  
  
// 测试反转类型  
const invertedObj: Inverted = {  
  '1': 'a',  
  '2': 'b'  
};  
  
// 这是一个类型守卫函数,用于在运行时检查对象是否符合 Inverted 类型  
function isInvertedType(obj: any): obj is Inverted {  
  return '1' in obj && '2' in obj && obj['1'] === 'a' && obj['2'] === 'b';  
}  
  
if (isInvertedType(invertedObj)) {  
  console.log('invertedObj 符合 Inverted 类型');  
} else {  
  console.log('invertedObj 不符合 Inverted 类型');  
}
  • Invert<T> 类型别名通过条件映射
  • 使用了as关键字的映射类型语法,这在TypeScript 4.1及以后版本中可用
  • 来生成一个新的类型,该类型实质上是原类型T中每个键值对的倒置
  • 但这种方式更加灵活,因为它基于值的类型来进行映射
  • 在这个例子中,要求原对象的值必须是字符串,从而可以用作新类型的键
  • 因此,虽然Inverted类型在功能上类似于某种Record类型的应用
  • 但它更准确地描述为一个经过类型系统转换处理后得到的、具有特定键值对的对象类型,而不是直接等同于使用Record定义的类型

6 )自定义Record(硬编码联合类型映射)

示例

// 1. 使用 TypeScript 内置的 Record 类型  
type UserRecord = Record<string, any>; // 允许任何字符串作为键,任何类型作为值  
  
// 假设我们有一个具体的键集合和值类型  
type SpecificKeys = 'username' | 'age';  
type SpecificUserRecord = Record<SpecificKeys, string>; // 键必须是 'username' 或 'age',值必须是 string  
  
// 2. 自定义一个类似 Record 的类型,但键是硬编码的(这不是一个好的做法,因为它不够灵活)  
type MyRecord<T> = {  
  [P in 'username' | 'age']: T; // 硬编码的键 'username' 和 'age',值类型由泛型 T 指定  
};
  
// 使用自定义的 MyRecord 类型
type MyUserRecord = MyRecord<string>; // 键是 'username' 或 'age',值都是 string  
  
// 示例对象  
const user: SpecificUserRecord = {  
  username: 'Alice',  
  age: '30' // 注意:这里为了演示使用了字符串,但在实际应用中年龄应该是数字  
};  
  
const myUser: MyUserRecord = {  
  username: 'Bob',  
  age: '25' // 同样,这里使用了字符串作为示例  
};  
  
// 说明:  
// 1. 内置的 Record 类型更加灵活,允许您指定任何键的集合(K extends keyof any)和值的类型(T)。  
// 2. 自定义的 MyRecord 类型在这里是硬编码的,它只允许 'username' 和 'age' 作为键,并且值的类型由泛型 T 指定。  
// 3. 在实际应用中,通常推荐使用内置的 Record 类型,因为它更加灵活且易于维护。

7 ) 类型谓词与模式匹配

示例

function isRecordOfStrings(obj: any): obj is Record<string, string> {
  return typeof obj === 'object' && obj !== null && Object.values(obj).every(v => typeof v === 'string');
}

if (isRecordOfStrings(someObj)) {
  // 在此块内,someObj 被认为是 Record<string, string>
}
  • 通过类型谓词和 Record 结合,可以实现更加精准的类型判断和模式匹配逻辑

8 ) Record 与 索引签名

示例

// 假设我们有一个Goods类型  
type Goods = {  
  id: number;  
  name: string;  
};  
  
// TypeScript内置的Record类型  
type RecordType<K extends keyof any, T> = {  
  [P in K]: T;  
};  
  
// 使用Record类型来创建一个新的类型,该类型的属性与Goods的键相同,但值都是string类型  
type resultGoodsType = RecordType<keyof Goods, string>;  
// 这等价于:  
// type resultGoodsType = {  
//   id: string;  
//   name: string;  
// };  
  
// 类似索引签名的类型定义(但不是Record)  
type IndexSignatureType = {  
  [x: string]: any; // 字符串索引签名,允许任何字符串作为键,值是any类型  
  // 注意:一旦你定义了字符串索引签名,那么就不能再为特定的属性名定义更具体的类型了  
  // 因为所有属性名最终都会被视为字符串,并遵循这个索引签名的类型  
};  
  
// 索引签名不能包含number或symbol作为键类型(除了特殊的数组和Map类型)  
// type BadIndexSignatureType = {  
//   [x: number]: any; // 错误:索引签名参数类型必须为 "string" 或 "number" 或 "symbol"。  
// };  
  
// 使用Record和索引签名的区别  
// Record类型允许你明确指定键的类型和值的类型  
// 而索引签名则提供了一种更通用的方式来描述对象的结构,但会限制你对特定键的类型定义  
  
// 示例:使用Record和索引签名  
const recordExample: resultGoodsType = {  
  id: '123', // 符合resultGoodsType的定义,因为id现在是string类型  
  name: 'Apple' // 符合resultGoodsType的定义,因为name现在是string类型  
};  
  
const indexSignatureExample: IndexSignatureType = {  
  id: 123, // 符合IndexSignatureType的定义,因为id被视为字符串键,但其值可以是any类型  
  name: 'Banana', // 符合IndexSignatureType的定义  
  'some-other-key': true // 也符合,因为所有字符串键都符合索引签名的定义  
};

联系:

  • Record类型和索引签名都用于描述对象的结构
  • 它们都允许你使用类型参数来定义键和值的类型

区别:

  • Record是一个内置工具类型,它接受两个类型参数来明确指定键和值的类型
  • 索引签名是TypeScript中对象类型定义的一部分,它允许你定义一个或多个类型的键(通常是string或number),并为这些键指定一个值的类型
  • 一旦你定义了一个字符串索引签名,那么你就不能再为对象的特定属性定义更具体的类型了(除非该类型与索引签名的类型兼容)
  • 这是因为所有属性名最终都会被视为字符串,并遵循这个索引签名的类型。
  • Record类型提供了一种更精确和可控的方式来定义对象的结构,而索引签名则提供了一种更通用和灵活的方式来描述对象的结构

9 )实现轻量级 Map

type SimpleMap<KeyType extends string | number | symbol, ValueType> = {
  [key in KeyType]: ValueType;
};

class LightMap<KeyType extends string | number | symbol, ValueType> {
  private map: SimpleMap<KeyType, ValueType>;

  constructor() {
    this.map = {} as SimpleMap<KeyType, ValueType>;
  }

  set(key: KeyType, value: ValueType): void {
    this.map[key] = value;
  }

  get(key: KeyType): ValueType | undefined {
    return this.map[key];
  }

  delete(key: KeyType): boolean {
    const hasKey = key in this.map;
    if (hasKey) {
      delete this.map[key];
    }
    return hasKey;
  }

  has(key: KeyType): boolean {
    return key in this.map;
  }
}

const myMap = new LightMap<string, number>();

myMap.set('apple', 1);
myMap.set('banana', 2);

console.log(myMap.get('apple')); // 输出:1
console.log(myMap.has('cherry')); // 输出:false
console.log(myMap.delete('banana')); // 输出:true
console.log(myMap.has('banana')); // 输出:false
  • Record是TypeScript的一个内置类型别名,它允许你基于一个索引类型(键)和一个值类型创建一个对象类型
  • 例如,Record<string, number>定义了一个所有键为字符串、值为数字的对象类型
  • 我们的轻量级Map实现将包括以下几个基本操作:
    • 设置值: set(key, value)
    • 获取值:get(key)
    • 删除值:delete(key)
    • 检查找键是否存在:has(key)
  • 泛型SimpleMap,允许键为string、number或symbol,值为任意类型ValueType
  • 通过TypeScript的Record类型和类,我们实现了一个轻量级的Map数据结构,它在编译时提供类型安全检查,确保了键值的类型正确性
  • 虽然它不具备JavaScript内置Map对象的所有功能(如迭代器、容量自动扩展等),但足以应对一些简单场景,且在类型安全方面提供了额外保障

相关推荐

  1. Typescript高级: 深入实践Record类型

    2024-06-05 23:42:04       33 阅读
  2. Typescript高级: 深入理解Extract类型

    2024-06-05 23:42:04       32 阅读
  3. TypeScript高级类型简介以及使用方法】

    2024-06-05 23:42:04       33 阅读
  4. TypeScript的基础类型高级类型梳理总结

    2024-06-05 23:42:04       43 阅读
  5. TypeScript 类型系统深度解析:类型全览

    2024-06-05 23:42:04       35 阅读
  6. .NET 使用Automapper映射 Record类型

    2024-06-05 23:42:04       26 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-05 23:42:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-05 23:42:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-05 23:42:04       87 阅读
  4. Python语言-面向对象

    2024-06-05 23:42:04       96 阅读

热门阅读

  1. 25台es集群停止步骤

    2024-06-05 23:42:04       26 阅读
  2. EmmyLua注释详解

    2024-06-05 23:42:04       29 阅读
  3. 【MySQL】——数据库设计概述与需求分析

    2024-06-05 23:42:04       32 阅读
  4. 全面而实用的敬酒词大全指南

    2024-06-05 23:42:04       24 阅读
  5. 1初识C#

    2024-06-05 23:42:04       25 阅读
  6. C++ Qt:QString与数字之间的相互转换

    2024-06-05 23:42:04       29 阅读