🎨
Notes
  • 持续更新中...
  • articles
    • browser
      • 深入理解现代浏览器 - 导航
      • 深入理解现代浏览器 - 架构
      • 深入理解现代浏览器 - 交互
      • 深入理解现代浏览器 - 渲染器进程
    • dsa
      • DSA - 并查集
      • DSA - 哈希表
      • DSA - AVL 树
      • DSA - 二叉树
      • 快速选择
      • Big O 算法复杂度
      • DSA - 栈和队列
      • DSA - 前缀树 Trie
      • DSA - 图
      • DSA - 链表
      • DSA - 递归
    • typescript
      • TypeScript 学习笔记 - 任意属性 (Indexable Types)
      • 力扣的 TypeScript 面试题
      • TypeScript 学习笔记 - as const
      • TypeScript 学习笔记 - infer
    • network
      • Internet Protocol (IP)
      • 计算机网络基础
      • 如何分辨同源和同站
      • DNS 如何查询 IP 地址?
    • vue
      • Nuxt.js 入门
      • 从零实现一个 Mini Vue
      • 从零实现一个简单的 VDOM 引擎
      • 从零实现一个响应式状态管理
    • sorting
      • 排序 - 归并排序
      • 排序 - 冒泡排序
      • 排序 - 选择排序
      • 排序 - 计数排序
      • 排序 - 插入排序
    • compile
      • Compiler and Interpreter
      • Just-In-Time (JIT) Compilers
      • 编译流程
    • others
      • 什么是上下文无关语法
      • 如何在终端打印出有颜色的字
    • dev-ops
      • github-actions
        • GitHub Action 简介
        • GitHub Actions for CI
    • workflow
      • 用 Node 写一个 cli
      • 如何规范 git commit 信息
      • 如何监听 git hooks
      • 如何规范代码风格 - prettier
      • 如何发布一个 npm package
      • 如何规范代码质量 - eslint
    • design-pattern
      • 代理模式
      • 单例模式
      • 策略模式
    • security
      • 点击劫持
      • CSP 内容安全策略
    • javascript
      • 尾调用优化
      • 4种常见的内存泄漏及解决方法
    • unit-test
      • Test Vuejs Application - Chapter 2
      • Test Vuejs Application - Chapter 1
      • Vue Unit Test Intro
    • performance
      • HTTP 缓存
      • 如何优化图片资源
Powered by GitBook
On this page
  • string 类型任意属性
  • number 类型任意属性
  • 同时定义两种任意属性
  • 同时定义任意属性和其他类型的属性

Was this helpful?

  1. articles
  2. typescript

TypeScript 学习笔记 - 任意属性 (Indexable Types)

PrevioustypescriptNext力扣的 TypeScript 面试题

Last updated 4 years ago

Was this helpful?

我们在自定义类型的时候,有可能会希望一个接口允许有任意的属性签名,这时候 任意属性 就派上用场了。

任意属性有两种定义的方式:一种属性签名是 string 类型的,另一种属性签名是 number 类型的。

string 类型任意属性

第一种,属性签名是 string,比如对象的属性:

interface A {
    [prop: string]: number;
}

const obj: A = {
    a: 1,
    b: 3,
};

[prop: string]: number 的意思是,A 类型的对象可以有任意属性签名,string 指的是对象的键都是字符串类型的,number 则是指定了属性值的类型。

prop 类似于函数的形参,是可以取其他名字的。

number 类型任意属性

第二种,属性签名是 number 类型的,比如数组下标:

interface B {
    [index: number]: string;
}

const arr: B = ['suukii'];

[index: number]: string 的意思是,B 类型的数组可以有任意的数字下标,而且数组的成员的类型必须是 string。

同样的,index 也只是类似于函数形参的东西,用其他标识符也是完全可以的。

同时定义两种任意属性

需要注意的是,一个接口可以同时定义这两种任意属性,但是 number 类型的签名指定的值类型必须是 string 类型的签名指定的值类型的子集,举个例子:

interface C {
    [prop: string]: number;
    [index: number]: string;
}

// Numeric index type 'string' is not assignable to string index type 'number'.

上面定义是不成立的,因为 index 指定的值类型是 string,而 prop 指定的值类型是 number,string 并不是 number 的子集。

如果换成下面这样,定义就是成立的,因为 Function 是 object 的子集:

interface C {
    [prop: string]: object;
    [index: number]: Function;
}

同时定义任意属性和其他类型的属性

另外还有一个需要注意的点,一旦定义了任意属性,那么其他属性(确定属性、可选属性、只读属性等)的类型都必须是它的类型的子集。

比如说我们想要一个 Person 接口,它有一个必选属性 name 和一个可选属性 age,另外还可以有其他 string 类型的任意属性签名。那么 Person 接口可能会被定义成这样:

interface Person {
    name: string;
    age?: number;
    [prop: string]: string;
}

// Property 'age' of type 'number' is not assignable to string index type 'string'.

但其实这样子的定义是不成立的,因为 [prop: string]: string 的存在,规定了其他属性的类型也必须是 string,如果想要解决报错,我们可以使用联合类型:

interface Person {
    name: string;
    age?: number;
    [prop: string]: string | number;
}

对于 number 类型的任意属性签名,情况也是一样的:

type MyArray = {
    0: string;
    [index: number]: number;
};
// Property '0' of type 'string' is not assignable to numeric index type 'number'.

但是,number 类型的任意属性签名不会影响其他 string 类型的属性签名:

type Arg = {
    [index: number]: number;
    length: string;
};

如上,虽然指定了 number 类型的任意属性的类型是 number,但 length 属性是 string 类型的签名,所以不受前者的影响。

但是反过来就不一样了,如果接口定义了 string 类型的任意属性签名,它不仅会影响其他 string 类型的签名,也会影响其他 number 类型的签名。这一点可以参考两种任意类型签名并存时,number 类型的签名指定的值类型必须是 string 类型的签名指定的值类型的子集这句话。

官方文档