仅知道键和值的类型,如何定义 TS 对象类型?

开发 前端
这里我们可以实现一个 calculateSalary 函数来计算他们的薪资,计算逻辑实现起来很简单。但问题来了,在 TS 中如何定义该函数参数的类型呢?

在学习 TS 的过程中,你遇到过类似的错误么?

let user = {}
user.id = "TS001" // 类型“{}”上不存在属性“id”。
user.name = "阿宝哥" // 类型“{}”上不存在属性“name”。

除了 any “大招” 之外,你还知道几种解决方案?阅读完本文,你将会找到一些答案。

这是小王他的月薪是包含了基本工资和月度奖金,这是小郭他的月薪只包含合同工资。

const Wang = {
baseSalary: 10000, // 基本工资
monthlyBonus: 2000 // 月度奖金
};
const Guo = {
contractSalary: 15000 // 合同工资
}

这里我们可以实现一个 calculateSalary 函数来计算他们的薪资,计算逻辑实现起来很简单。但问题来了,在 TS 中如何定义该函数参数的类型呢?

function calculateSalary(salaryObject: ???) {
let total = 0;
for (const name in salaryObject) {
total += salaryObject[name];
}
return total;
}

给你 3 秒钟的时间思考一下。你想到答案了么?其中一种方案是使用索引签名。当我们仅知道对象键和值的类型时,就可以使用索引签名来定义该对象的类型。

这是索引签名的语法:

{ [key: KeyType]: ValueType }

其中 Key 的类型,只能是 string,number,symbol 或模版字面量类型,而值的类型可以是任意类型。

interface Dictionary {
[key: boolean]: string;
}

其中模版字面量类型是 TypeScript 4.1 版本引入的新类型,结合索引签名我们可以定义更强大的类型:

interface PropChangeHandler {
[key: `${string}Changed`]: () => void;
}
let handlers: PropChangeHandler = {
idChanged: () => {}, // Ok
nameChanged: () => {}, // Ok
ageChange: () => {} // Error
};

了解索引签名的语法之后,我们就可以轻松地定义出 salaryObject 参数的类型:

function calculateSalary(salaryObject: { [key: string]: number }) {
let total = 0;
for (const name in salaryObject) {
total += salaryObject[name];
}
return total;
}

有些时候,在定义对象类型时,会含有一些已知和未知的键,这时我们可以结合索引签名来定义该对象的类型:

interface Options {
[key: string]: string | number | boolean;
timeout: number; // 已知的键
}
const options: Options = {
timeout: 1000,
errorMessage: 'The request timed out!',
isSuccess: false
};

在使用索引签名时,你可能会遇到这些困惑:

interface NumbersNames {
[key: string]: string
}
const names: NumbersNames = {
'1': 'one',
'2': 'two',
'3': 'three'
};
const value1 = names['1'] // Ok
const value2 = names[1] // Ok

type N0 = keyof NumbersNames // string | number
  • 为什么可以通过字符串 1 和数字 1 来访问对应的属性值。
  • 为什么 keyof NumbersNames 返回的 string 和 number 类型组成的联合类型。

这是因为当用作属性访问器中的键时,JavaScript 会隐式地将数字强制转换为字符串,TypeScript 也会执行这种转换。

除了使用索引签名之外,我们还可以使用 TS 内置的工具类型 Record 类型来定义 calculateSalary 函数的参数类型:

type Record<K extends keyof any, T> = {
[P in K]: T;
};
function calculateSalary(salaryObject: Record<string, number>) {
let total = 0;
for (const name in salaryObject) {
total += salaryObject[name];
}
return total;
}

那么索引签名和 Record 工具类型有什么区别呢?在一些场合下,它们都能定义出期望的类型。

const user1: Record<string, string> = { name: "阿宝哥" }; // Ok
const user2: { [key: string]: string } = { name: "阿宝哥" }; // Ok

对于索引签名来说,其键的类型,只能是 string,number,symbol 或模版字面量类型。而 Record 工具类型,键的类型可以是字面量类型或字面量类型组成的联合类型:

type User1 = {
[key: "id"]: string; // Error
};
type User2 = {
[key: "id" | "name"]: string; // Error
};
type User3 = Record<"id", string>; // Ok
type User4 = Record<"id" | "name", string>; // Ok
const user: User4 = {
id: "TS001",
name: "阿宝哥",
};

看完以上内容,你学会索引签名和 Record 工具类型了么?

责任编辑:姜华 来源: 全栈修仙之路
相关推荐

2022-08-08 23:49:01

TypeScriptumd模块

2011-08-02 17:53:23

Oracle对象类型对象类型表

2009-10-10 09:53:07

.NET值类型

2009-08-31 14:34:46

C#值类型C#结构类型

2022-01-19 23:41:56

TS索引类型

2009-08-19 16:39:44

C#值类型C#引用类型

2009-08-26 14:05:19

C#值类型和引用类型

2021-08-27 07:47:06

引用类型

2023-05-31 08:24:20

SQLAlchemyPython

2024-08-19 09:07:09

TSvoid类型

2022-02-12 22:16:53

TypeScript类型字符串

2020-03-23 08:15:43

JavaScriptError对象函数

2022-08-14 06:59:01

TS 类型编程索引类型

2022-02-25 14:04:56

TS前端代码

2022-04-29 06:54:48

TS 映射类型User 类型

2011-06-08 13:03:52

C#值类型引用类型

2022-05-07 07:33:55

TypeScript条件类型

2022-12-30 08:08:30

2013-05-23 17:21:16

C#

2021-06-01 23:18:00

Golang语言Method
点赞
收藏

51CTO技术栈公众号