TypeScript类型种类如下
TypeScript基本类型:
number,string,boolean,bigint,symbol,null,undefined,never,void,any,unknown,值类型
对象类型:
class,object,Array,interface,JavaScript构造函数类型
高级类型:
enum,tuple,type,联合类型,交叉类型
大小写区别:
TypeScript类型和JavaScript类型大小写不同。
性能区别:
JavaScript中这些类型是作为构造函数或工厂函数使用,TypeScript这些类型就是纯粹的类型,没有这些功能。虽然使用两者进行注释是等效的,但是使用TypeScript中这些类型会有更好的性能和内存管理能力(参考ChatGPT)。
never和void是语义相近的类型,never比void更加严格。
never表示不能是任何类型:
// 使用后文提到的“类型别名”和“交叉类型”来获得never类型
type variableA = string
type variableB = number
//得到的variableC是never类型,因为string和number类型不可能取交集
type variableC = variableA & variableB
// 指定一个函数返回值为never类型,这个函数永远不能被赋值,因为函数默认返回undefined
let funcEmpty: () => never
void表示值只能是undefined:
// 指定一个函数为空函数
let funcEmpty: () => void = () => {}
any表示任何类型:
any表示任何类型,不推荐使用,使用后效果和JavaScript相同。
unknown表示未知类型,操作需要检查:
unknown表示任何类型,含义和any相同,但是比any严格,可以使用。在调用变量属性方法,或进行其它操作时不经过检查都是不合法的。
let variable: unknown = 'hello'
console.log(variable.length) // 报错,不能对unknown类型变量进行操作
if(typeof variable === 'string')
console.log(variable.length) // 合法,提前检测了类型
当声明时指定了具体值之后,变量赋值只能赋为该值,否则会发生错误
let variableA: 2
variableA = 2
直接进行类型注释即可,较为简单
let variable: string = 'hello'
不要使用object和Object进行注释:
在1.1.1中提到了用object和Object进行类型注释是等效的。但是实际上不推荐使用object和Object进行类型注释。使用object和Object进行对象类型注释,TypeScript会认为对象不可修改,必须保持和空对象一致。
let obj: object = {
name: "Danny",
age: 21
}
console.log(obj.name) // 报错,不允许访问name属性
obj.gender = "male" // 报错,不允许修改对象
console.log(obj.toString) // 合法,空对象原型上有toString方法
使用对象字面量进行注释:
如果要类型注释对象类型,推荐使用对象字面量。不过要注意的是,TypeScript是“静态类型检查语言”(参考编译原理),对于类型检查是严格的,在变量初始化时指定了要和对象字面量形式一致,因此下面添加或删除对象属性都是非法的。
let obj: { name: string, age: number } = {
name: "Danny",
age: 21
}
console.log(obj.name) // 合法
obj.gender = "male" // 不合法
console.log(obj.toString) // 合法
其它推荐的对象注释方式:
在实际应用时使用对象字面量注释不具有复用性,通常使用“接口”,“类型别名”,“类”进行注释
TypeScript中数组类型注释较为简单。string[]
类型注释是泛型类型注释Array
的简写,效果相同。
在使用Array时要另外注意一点Array类型在类型推断时会被推断为数组元素个数不确定,详情参考“2.2.2数组推断”或“TypeScript泛型”博文。
// 类型注释一维数组
let ar1Dim: string[] = ['1', '2', '3']
// 类型注释二维数组
let ar2Dim: string[][] = [['11', '12', '13'], ['21', '22', '23']]
// 还可以使用内置Array泛型来声明,但是声明高维数组较麻烦
const ar1Dim: Array<string> = ['1', '2', '3']
const ar2Dim: Array<Array<string>> = [['11', '12', '13'], ['21', '22', '23']]
“函数表达式”声明函数:
function func(arg1: string, arg2: number): void {}
“赋值语句”声明函数:
const func = function(arg1: string, arg2: number): void {}
“箭头函数”声明函数:
const func = (arg1: string, arg2: number): void => {}
“接口或类型别名”声明函数:
// 接口
interface FuncInterface {
(arg1: string, arg2: number): void // 语法糖:对象字面量中定义函数返回值可以用“:返回值”替代
}
const funcI: FuncInterface = (arg1: string, arg2: number) => {}
// 类型别名
type FuncAlias = (arg1: string, arg2: number) => void // 语法糖:描述箭头函数类型
const funcA = (arg1: string, arg2: number) => {}
“函数签名”声明函数:
// 函数签名可以由“接口”,“类型别名”实现
interface Func {
description: string;
(arg1: string, arg2: number): void
}
function func(arg1: string, arg2: number): void {}
func.description = "this is a function"
const funcS: Func = func
不要使用Function:
不要使用Function进行类型注释:在最后两种声明方法中和“object和Object”一样,在这里也不推荐用Function进行注释。Function太过于宽泛,没有指明参数类型和返回值类型。
使用throw可以不用考虑返回值:
如果函数中使用throw,说明函数无法正常返回值,当然默认返回undefined也是不生效的。在这里已经抛出了错误,所以不需要考虑和函数返回值的类型注释保持一致。
function test(): string {
throw new TypeError('test') // 合法
}
test() // 合法
在描述接口换行时可以使用逗号或分号
interface STU {
id: string,
name: string,
gender: string,
age: number,
getDetailInfo(id: string): { grade: number, location: string }
}
使用extends关键字扩展接口:
interface A {
name: string
}
interface B extends A { // 此时实现接口B也必须含有接口A中的name
age: number
}
使用覆写方式扩展接口:
interface A {
name: string
}
interface A {
age: number // 此时效果和extends效果相同
}
使用交叉类型扩展接口:
interface A {
name: string
}
interface B {
age: number
}
type C = A & B
使用implements实现接口无法进行类型推断:
后文要提到的“类型推断”是指在不进行类型注释时TypeScript会推测变量类型是哪一种,这类似于JavaScript的V8引擎中的“JIT”优化中的热处理(详情参考博客“JavaScript编译器和解释器”)。由于这里只有implements关键字,TypeScript无法推测类实现的接口中的方法的参数类型。
class SDUer implements STU { // STU接口在上文已经实现
constructor(public id: string, public name: string, public gender: string, public age: number) {}
getDetailInfo(id) {
return {
grade: 100,
location: '6'
}
}
}
let sduer = new SDUer('123', 'Danny', 'male', 21)
sduer.getDetailInfo(1234) // 虽然接口中指明了该方法接收的参数应为字符串,但是编译器不会报错
解决类实现接口可能出现的潜在错误:
由于implements导致无法进行类型推断,因此函数参数默认为any,这样会影响静态类型检查,可能会出现错误。
可行的解决方案:
配置tsconfig.json,开启严格模式,这样不允许any类型出现
实现接口方法时主动添加参数类型
getDetailInfo(id: string) {
return {
grade: 100,
location: '6'
}
}
声明对象时指定对象就是接口的实现
let sduer: STU = new SDUer('123', 'Danny', 'male', 21)
类不能实现接口描述的构造函数:
接口或类型别名可能使用这样的语法糖来描述构造函数:
interface Constructor {
new (arg1: string, arg2: number): { name: string, age: number }
}
type Constructor = {
new (arg1: string, arg2: number): { name: string, age: number }
}
但是类不能实现这样的接口,因为构造函数属于类的独有成员,不应该由接口描述,不论尝试怎样实现,编译器都会报错。
接口描述构造函数的场景应用较少,比较常见的一个场景就是“使用工厂函数创建对象
// 通过接口来描述构造函数
interface Constructor {
new (name: string, age: number): { name: string, age: number }
}
// 构造构造函数所在的类
class Person {
constructor(public name: string, public age: number) {}
}
// 工厂函数
function factory(constructor: Constructor, name: string, age: number): Person {
return new constructor(name, age)
}
// 构造函数通过类名调用,所以将Person类传递过去
console.log(factory(Person, 'Danny', 21))
const sduStu: STU = {
id: '1234',
name: 'Danny',
gender: 'male',
age: 21,
getDetailInfo(id: string) { // id可以不写类型注释,因为通过sdustu: STU可以推断出这里的id必须是string类型
return {
grade: 100,
location: '6'
}
}
}
函数实现接口的方法起始已经委婉地在上1.2.3中提到,就是“接口声明函数”和“函数签名声明函数”两种方式。
// 用接口声明函数
interface Cal {
(a: number, b: number): number
}
const cal: Cal = (a, b) => a + b
直接进行注释即可,较为简单
// JavaScript构造函数类型
let str: String = 'hello'
// 自定义class类型
class STU {
public name: string
public age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
let stu: STU = new STU("Danny", 21)
使用enum声明一个枚举,枚举中又声明了若干枚举值,每一个枚举值又会有一个关联值。
默认从第一个枚举值开始关联0,往后关联1,2,3
enum Fruit {
Apple,
Orange,
Melon
}
typescript的默认枚举在实现上实际是一个对象,对象中进行了反向映射
let Fruit = {
Apple: 0,
Orange: 1,
Melon: 2,
'0': 'Apple',
'1': 'Orange',
'2': 'Melon'
}
Fruit.Apple
或Fruit[0]
这两种访问形式都有应用场景
数值枚举要求给枚举值赋值为数值
enum Fruit {
Apple = 2,
Orange,
Melon
}
反向映射结果为
let Fruit = {
Apple: 2,
Orange: 3,
Melon: 4,
'2': 'Apple',
'3': 'Orange',
'4': 'Melon'
}
也可以给全部枚举值指定关联值
enum Fruit {
Apple = 2,
Orange = 1,
Melon = 4
}
反向映射结果为
let Fruit = {
Apple: 2,
Orange: 1,
Melon: 4,
'2': 'Apple',
'1': 'Orange',
'4': 'Melon'
}
字符串枚举要求每个枚举值必须赋一个字符串关联值
enum Fruit {
Apple = 'Apple',
Orange = 'Orange',
Melon = 'Melon'
}
反向映射结果为
let Fruit = {
Apple: 'Apple',
Orange: 'Orange',
Melon: 'Melon',
}
混合枚举要求每个枚举值必须赋一个字符串或数值关联值。不推荐使用混合枚举,一般枚举值都有相关性,混合枚举破坏了相关的联系。
enum Fruit {
Apple = 0,
Orange = 'Orange',
Melon = 'Melon'
}
反向映射结果为
let Fruit = {
Apple: '0',
0: 'Apple',
Orange: 'Orange',
Melon: 'Melon'
}
常量枚举在编译时会被删除,不产生枚举对应的对象形式,效率较高。因此声明常量枚举后直接访问枚举是非法的,只能访问枚举值。
const enum Fruit {
Apple,
Orange,
Melon
}
console.log(Fruit) // 非法的
console.log(Fruit.Apple) // 合理的
因为不会生成对象形式,因此最后结果只会输出0
在typescript中枚举,数组,对象相互之间的功能比较相似,需要在特定场景进行分析。
语意明确时用枚举:
当常量具有清晰明确的语意时,应该使用枚举类型。枚举类型为常量提供了明确的命名和类型,可以使代码更加可读和维护。
enum DaysOfWeek {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
function isWeekend(day: DaysOfWeek): boolean {
return day === DaysOfWeek.Saturday || day === DaysOfWeek.Sunday;
}
isWeekend(DaysOfWeek.Saturday); // true
值不需要变动用对象或数组:
如果固定的值不会发生变化,可以考虑使用常量数组或对象。
const weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];
const maxFileSize = { txt: 1024, jpg: 2048, gif: 3072 };
值需要从动态来源获得用对象或数组:
如果值需要从动态来源获取,例如从后端API中获取,则应该使用对象或数组类型,并根据需要进行类型定义。
interface User {
id: number;
name: string;
role: "admin" | "member";
}
function hasAdmin(users: User[]) {
return users.some(user => user.role === "admin");
}
// 从API中获取用户信息
fetch("/users")
.then(response => response.json())
.then(data => {
if (hasAdmin(data)) {
// do something
}
});
枚举和枚举值都可以作为类型来进行注释。
(常用)注释为枚举类型:
将变量注释为枚举类型,指的是变量的取值范围固定在枚举值的关联值当中。
let variableEnum: Fruit = 0
if(variableEnum === Fruit.Apple)
console.log('Apple')
else if(variableEnum === Fruit.Orange)
console.log('Orange')
else if(variableEnum === Fruit.Melon)
console.log('Melon')
else
console.log('None')
注释为枚举值类型:
let variableA: Fruit.Apple
variableA = 0
元组确定性:
元组确定了数组每个元素的类型以及数组的长度。数组只能规定每个元素的类型,不能确定数组长度。
const tuple: [number, number] = [1, 2]
const ar: Array<number> = [1, 2]
const add = (a: number, b: number) => a + b
add(...tuple) // 合法,元组长度固定,展开后add接收两个参数
add(...ar) // 不合法,数组长度不确定,展开后接收未知个参数
元组可选参数:
在进行元组类型的注释时可以使用?表示元素可以不存在,但是只能在最后使用。
type AnyTypeArray = [number, boolean?, string?]
const ar: AnyTypeArray = [1] // 合法
const ar: AnyTypeArray = [1, true] // 合法
如果没有使用元组的可选元素,那么默认为undefined。元组的长度会包括类型注释时的可选参数。
type AddParameters = [number, number, number?]
const ar: AddParameters = [1, 2]
const add = (a: number, b: number) => a + b
add(...ar) // 报错,元组长度实际为3
元组剩余参数:
在进行元组类型注释时可以在非尾部使用剩余参数。
type Tuple = [number, ...boolean[], string]
const ar: Tuple = [1, 'hello']
const arr: Tuple = [1, true, false, 'hello']
☆☆这种做法还可以实现JavaScript无法做到的函数形参声明。相当于可以在非末尾声明剩余形参。
function handleUserInfo(...args: [number, ...string[], boolean]) {
const [age, name, gender, location, isMarried] = args
console.log(age, name, gender, location, isMarried)
}
// 使用效果相当炸裂
handleUserInfo(21, 'Danny', 'male', 'WeiHai', true)
只读元组:
只读元组可以使用TypeScript内置的Readonly别名来声明
const tuple: Readonly<[number, string]> = [21, 'Danny']
const [userAge, userName] = tuple
只读元组可以使用readonly关键字来声明
const tuple: readonly [number, string] = [21, 'Danny']
const [userAge, userName] = tuple
使用类型别名指定已经存在的类型
// 使用类型别名指定为对象字面量类型
type School = {
name: String,
age: Number
}
let variable: School
// 使用类型别名指定为TypeScript基本类型
type str = string
类型别名一旦声明就只能通过&运算符进行扩展
type A = {
name: string
}
type B = {
age: number
}
type C = A & B // 合法
B.name: string // 非法
联合类型指的是用|来表示变量可以取不同的类型
let variableA: string | number | boolean
交叉类型指的是用&来表示变量取多种类型中的交集。
对于接口,对象字面量类型:
使用&交叉会合并属性,因为可以做到同时满足。
interface A {
name: string
}
interface B {
gender: string
}
type C = A & B
let obj: C = {
name: 'Danny',
gender: 'male'
}
对于普通类型:
使用&交叉会直接取交集,因为无法同时满足。
type TypeA = string | number
type TypeB = number | boolean
type TypeC = TypeA & TypeB
let variable: TypeC = 123 // variable只能取数值类型
合理利用类型推断可以使代码更简洁。不过要注意,尽可能不要被推断为any,在typescript严格模式下是不允许的。
下面函数的返回值一定是number类型,这是根据已有类型注释进行推断。
下面对象字面量实现接口,因为接口中已经显示指定类型且对象指定为接口的实现,那么实现的函数的参数将会被自动进行类型推断。
function cal(a: number, b: number) {
return a + b
}
interface Test {
work(a: number, b: number): void
}
const TestMaker: Test = {
work: (a, b) => {}
}
下面会根据“hello”推测str为字符串类型。注意:除非显式指定变量为JavaScript值类型,否则不会被推测为JavaScript值类型。即下面的str不会被推测为“hello”类型。
let str = "hello"
类使用implements实现接口:
例如使用类来实现一个接口中的check方法。注意:(tsc 5.1.3)由于没有类型注释,编译器无法进行类型推断。在实现接口方法时会认为参数均为any类型。
interface CheckAble {
check(name: string): boolean
}
class NameChecker implements CheckAble {
check(s) {
return true
}
}
let nc = new NameChecker
nc.check(123) // 编译不会报错
添加类型注释,更规范地实现接口。(1)可以在声明变量时添加类型注释,指明是实现接口的变量(2)可以在类实现check方法时对参数进行类型注释
interface CheckAble {
check(name: string): boolean
}
class NameChecker implements CheckAble {
check(s) {
return true
}
}
let nc: CheckAble = new NameChecker
nc.check(123) // 编译会报错
回调函数传参:
该内容会在“TypeScript函数”博文中详细介绍,指的是回调函数实参个数可以少于形参个数。
const filter = <T>(ar: Array<T>, callback: (el: T, idx: number, arr: Array<T>) => boolean): Array<T> => ar.filter(callback)
console.log(filter([1, 2, 3, 4], el => !!(el % 2))) // 只给callback传了第一个参数,少于形参要求的三个参数
字面量创建数组将被推测为数组:
在不进行类型注释时ar不会被推断为元组
const ar = [1, 2, 3]
数组长度推断:
在对数组进行推断时,默认认为数组类型大小是不固定的(参考“TypeScript对象”博文)。这也是为什么我们特别地提出元组中元素个数是确定的的原因。
const add = (a: number, b: number) => a + b
const ar = [1, 2]
console.log(add(...ar)) // 报错,推测ar为number数组类型且个数不确定,展开后不是确定的两个元素,与函数形参要求不匹配。
const ar = [1, 2] as const
console.log(add(...ar)) // 合法,使用类型断言将数组推断为元组,此时数组元素个数确定
值类型推断优先度最低,如果不显示地进行类型注释,则一定不会被推断为值类型
let str = "hello" // 推断为string类型而不是“hello”类型
let str2: "hello" = "hello" // 显示指定str2为“hello”类型
类型断言用在表达式之后,是一种显示类型转化的方法
类型注释优先级大于类型推断断言:
一旦进行了类型注释,再进行类型断言不会影响变量最终类型的推理。下面的例子虽然断言num是any,但是已经确认了num是number类型,所以最终num是number类型,访问length属性是非法的。
const num: number = 123 as any
num.length // 报错,num是number类型
下面的例子没有进行类型注释,此时使用类型断言会影响类型推断过程的走向。此时num被推测为any类型,访问length合法。
const num = 123 as any
num.length // 合法
☆☆下面的例子很有代表性,默认字面声明数组类型会被推断为数组,但是会认为数组长度未知,在使用展开符展开后作为实参传给函数,函数会认为你传递了未知数量的参数。这时候就需要类型断言,使用特殊的类型断言as const
说明对象是只读的,因此每个元素的类型固定下来,长度也固定下来,这时候数组会被推断为元组。这样就可以展开后作为剩余实参传给函数。
const ar = [1, 2] as const // as断言使得ar被推断为元组
const add = (a: number, b: number) => a + b
add(...ar) // 合法
☆☆下面的例子也有代表性。在理解了上面的例子后可以知道,一旦进行了类型注释,那么son就是Father类型。
interface Father {
name: string,
age: number
}
interface Son extends Father {
gender: string,
getInfo: () => void
}
const son: Father = {
name: 'Danny',
age: 21
} as Son
son.getInfo() // 报错,son是Father类型,没有getInfo方法
son.gender // 报错,son是Father类型,没有gender属性
类型推断断言不能用于无交集的类型:
类型断言可以将123推断为any,也可以将any推断为123。即可以在具象和非具象之间断言,但是不能在无关类型间断言。
下面的例子说明了字符串不可能被推断为数值。
const variable = 'hello' as number // 报错,我们希望将variable推断为number类型,但是这一定不可能发生
下面的例子说明了数组不可能被推断为元组。
const ar: Array<number> = [1, 2] as const // 报错,我们希望将ar推断为元组,但是已经指定了ar为数组,数组类型长度在TypeScript中被认为是不确定的,所以ar一定不可能被推断为元组
const arr: ReadonlyArray<number> = [1, 2] as const // 报错,理由同上(如果不了解as的机理,这种结果对于初学者可能难以理解)
显示类型转化:
除去JavaScript中的显示类型转化,TypeScript中显示类型转化有两种:(1)使用类型断言(2)在变量前使用<>改变其类型
const ar = [1, 2] as const
const ar = <const>[1, 2]
const ar: ReadonlyArray<number> = [1, 2, 3]
const arr: Array<number> = [4, 5, 6]
类型注释优先级大于非空赋值断言:
还是和类型推断断言as一样,类型注释一旦确定,我们无法再影响类型推断
const str: null = null !
console.log(str.length) // 报错,// 我们希望str被推断为非空,但是类型注释已经说明str为空,因此str一定不会被推断为非空
下面这个例子说明非空赋值断言的应用场景
const str = Math.random() + 0.5 > 0.5 ? 'hello' : null !
console.log(str.length) // 合法,Math.random() + 0.5一定大于0.5,我们希望str被推断为非空。否则str会被推断为联合类型
下面这个例子说明了非空赋值断言可以被类型推断断言替代
const str = (Math.random() + 0.5 > 0.5 ? 'hello' : null) as string
console.log(str.length) // 合法
keyof操作符用于获取对象的键的类型
keyof用法:
keyof操作符用于操作一个类型,返回该类型的所有键对应的类型。
interface User {
name: string,
age: number
}
type UserKey = keyof User
const user: UserKey = 'name' // 合法
const user2: UserKey = 'age' // 合法
const user3: UserKey = '123' // 非法,UserKey的类型就是"name" | "age",其它任何值都不能取
keyof操作对象:
注意类型操作符keyof操作的是类型,不能操作值
const obj = {
name: "Danny",
age: 21
}
type ObjAlias = keyof obj // 报错,obj是值不是类型
keyof操作结果:
因为对象的键是确定的,因此操作结果只可能是“数值类型”或“字符串值类型”或“符号值类型”。
const alias = Symbol('alias')
interface User {
name: string,
age: number,
[alias]: symbol,
[index: number]: string
}
type Type = keyof User
// 操作结果是符号,是值类型Symbol('alias')
const user: Type = alias
// 操作结果是字符串,是值类型“name”
const user2: Type = "name"
// 操作结果是number
const user3: Type = 123
typeof操作符用于获取值的类型
typeof用法:
typeof操作符与keyof操作符恰好相反,typeof操作符作用于值类型。
在TypeScript中修改了typeof操作符的功能,不再是像JavaScript中只返回字符串,而是会返回类型。
const num: number = 123
type Num = typeof num
const myNum: Num = 456
// typeof作用于对象不会返回object或Object
const user = {
name: 'Danny',
age: 21,
gender: 'male'
}
type User = typeof user
const myUser: User = {
name: 'Cathy',
age: 21,
gender: 'male'
}
索引签名指定了对象的键和值分别应该为什么类型
键只能指定为数值或字符串类型:
interface User {
[index: number]: string // 合法
}
interface User {
[index: string]: any // 合法
}
interface User {
[index: symbol]: string // 非法,索引签名中键的类型只能被指定为number或string
}
索引签名引起的冲突:
索引签名只应存在一个:
interface User {
[index: number]: string // 合法
[index: string]: string // 合法,但没有意义。number键名会默认转化为string类型
}
interface User {
[index: number]: string // 合法
[index: string]: number // 报错,索引签名不能同时满足值既是string又是number
}
索引签名和已有属性冲突:
interface User {
age: number, // 报错,下面的索引签名要求了所有键对应的值都应该为string类型
[index: string]: string
}
索引访问操作符用于获取对象值的类型
索引访问操作符用法:
索引访问操作符是[],其中接收某一种类型。通过这种类型去对象(接口或别名)中寻找其对应的值。
这里容易理解错误的是形如User["name"]
,你可能认为这里的“name”只是字符串,但是实际上它是“name”值类型。这也是解释了为什么在处理符号类型的键时要使用typeof alias
的原因,因为[]操作符中只能接收类型而不是值。
const alias = Symbol.for('alias')
interface User {
name: string,
age: number,
[alias]: boolean,
}
type UserAliasType = User[typeof alias] // 注意这里不是alias,是symbol.for('alias')类型,因为[]接收类型而不是值
type UserNameType = User["name"] // 这里的“name”不是字符串,而是“name”值类型
type UserAgeType = User["age"] // 这里的“age”同理也是“age”值类型
const myAlias: UserAliasType = true
const myName: UserNameType = 'Danny'
const myAge: UserAgeType = 21
索引访问操作符访问索引签名:
访问索引签名键对应的值时,可以通过number,string或具体的number值类型来进行访问
// 设定索引键为string
interface User {
name: string
[index: string]: string
}
type UserSignType = User[string] // 合法,通过string类型访问
const mySign: UserSignType = 'hello'
// 设定索引键为number
interface User {
name: string
[index: number]: string
}
type UserSignType = User[number] // 合法,通过number类型访问
type UserDetailType = User[1] // 合法,通过number值类型访问
const mySign: UserDetailType = 'hello'
复杂的类型提取示例(源自TypeScript官网)
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
type Age = typeof MyArray[number]["age"]; // 先通过typeof得到Array类型,再通过[number]访问索引类型得到{ name: string, age: number}类型,再通过["age"]索引访问得到number类型