我们用两个对象来描述两个码农的工资:
const salary1 = { baseSalary: 100_000, yearlyBonus: 20_000 }; const salary2 = { contractSalary: 110_000 };
然后写一个获取总工资的函数:
function totalSalary(salaryObject: ???) { let total = 0; for (const name in salaryObject) { total += salaryObject[name]; } return total; } totalSalary(salary1); // => 120_000 totalSalary(salary2); // => 110_000
如果是你的,要如何声明totalSalary()
函数的salaryObject
参数,以接受具有字符串键和数字值的对象?
答案是使用一个索引签名!
接着,我们来看看什么是 TypeScript 索引签名以及何时需要它们。
一、什么是索引签名
索引签名的思想是在只知道键和值类型的情况下对结构未知的对象进行类型划分。
它完全符合salary
参数的情况,因为函数应该接受不同结构的salary
对象,唯一的要求是属性值为数字。
我们用索引签名来声明salaryObject
参数:
function totalSalary(salaryObject: { [key: string]: number }) { let total = 0; for (const name in salaryObject) { total += salaryObject[name]; } return total; } totalSalary(salary1); // => 120_000 totalSalary(salary2); // => 110_000
{[key: string]: number}
是索引签名,它告诉 TypeScript salaryObject
必须是一个以string
类型为键,以 number
类型为值的对象。
二、索引签名语法
索引签名的语法相当简单,看起来与属性的语法相似,但有一点不同。我们只需在方括号内写上键的类型,而不是属性名称:{ [key: KeyType]: ValueType }
。
下面是一些索引签名的例子。
string
类型是键和值。
interface StringByString { [key: string]: string; } const heroesInBooks: StringByString = { 'Gunslinger': '前端博客', 'Jack Torrance': '' };
string
类型是键,值可以是 string
、number
或boolean
。
interface Options { [key: string]: string | number | boolean; timeout: number; } const options: Options = { timeout: 1000, timeoutMessage: 'The request timed out!', isFileUpload: false };
签名的键只能是一个 string
、number
或symbol
。其他类型是不允许的。
索引签名在键方面是通用的。
但是我们可以使用字符串字面值的联合来描述 Record<keys, Type>
中的键:
type Salary = Record<'yearlySalary'|'yearlyBonus', number> const salary1: Salary = { 'yearlySalary': 120_000, 'yearlyBonus': 10_000 }; // OK
Record<Keys, Type>
是为了具体到键的问题。
建议使用索引签名来注释通用对象,例如,键是字符串类型。但是,当你事先知道键的时候,使用Record<Keys, Type>
来注释特定的对象,例如字符串字面量'prop1' | 'prop2'
被用于键值。
总结
如果你不知道你要处理的对象结构,但你知道可能的键和值类型,那么索引签名就是你需要的。
索引签名由方括号中的索引名称及其类型组成,后面是冒号和值类型:{ [indexName: KeyType]: ValueType }
, KeyType
可以是一个 string
、number
或 symbol
,而ValueType
可以是任何类型。