给定一个接口,我们可以访问其特定密钥类型的属性,例如string:

interface AB {
  a: number;
  b: boolean;
}

type PropertiesByString = AB[keyof AB & string]

这是有效的,因为keyof AB & string就是"a" | "b",它们都是AB的有效密钥.相同的逻辑不适用于数组:

const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];
 
type Person = typeof MyArray[number];

该代码有效,但这里的number包括4等.为什么这里允许number

推荐答案

当你写

const myArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];
 

TypScript推断其为类型

// const myArray: { name: string; age: number; }[]

这只是一种简写的写作方式

// const myArray: Array<{ name: string; age: number; }>

所以myArrayarray type.如果你看the TypeScript type definition for Array<T>,它有一个数字index signature:

interface Array<T> {
    // ⋮ 
    [n: number]: T;
}

这意味着TypScript将允许您使用数字键对其进行索引.特别是,indexed access Array<T>[number]只会返回T.


您似乎在问为什么它没有抱怨,因为数组的contents并不是每个数字索引都有值.答案是因为类型不跟踪内容.

此外,形式{[key: K]: V}的索引签名并不意味着"该对象中存在类型K的每个键并且具有类型V的值". 相反,它意味着"如果该对象中存在类型K的键,那么其值是类型V."因此,(typeof myArray)[number]始终是{ name: string; age: number; },尽管如果您执行任意索引操作,您可能会在实践中看到undefined. 如果您希望将undefined视为索引操作的可能value,则可以打开--noUncheckedIndexedAccess:

const person = myArray[4];
// if --noUncheckedIndexedAccess is off
// const person: { name: string; age: number; } 

// if --noUncheckedIndexedAccess is on
// const person: { name: string; age: number; } | undefined 

但索引访问类型将保持不变.


tuple types个限制数组在特定索引处包含特定值类型,并且其长度是预先已知的.如果您在初始化数组文字上使用const assertion,您将获得更接近您可能正在寻找的类型的信息:

const myArrayConst = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
] as const;

/* const myArrayConst: readonly [{
    readonly name: "Alice";
    readonly age: 15;
}, {
    readonly name: "Bob";
    readonly age: 23;
}, {
    readonly name: "Eve";
    readonly age: 38;
}] */

现在TypScript知道length3,并且如果它看到您使用已知越界的字面数字进行索引,就会抱怨:

let personConst: Person;
personConst = myArrayConst[0]; // okay

personConst = myArrayConst[4]; // error! no element at index '4'
// error! Type 'undefined' is not assignable

但同样,这不会改变数字索引签名处发生的情况:

type PersonConst = (typeof myArrayConst)[number];
/* type PersonConst = {
    readonly name: "Alice";
    readonly age: 15;
} | {
    readonly name: "Bob";
    readonly age: 23;
} | {
    readonly name: "Eve";
    readonly age: 38;
} */

myArrayConst[Math.random()] // okay, number is still a valid index

仍然允许索引,并且索引访问仍然为您提供不含undefined的元素类型.

Playground link to code

Typescript相关问答推荐

类型安全JSON解析

对深度对象键/路径进行适当的智能感知和类型判断,作为函数参数,而不会触发递归类型限制器

Redux—Toolkit查询仅在不为空的情况下添加参数

如何在TypeScrip中从字符串联合类型中省略%1值

Vue SFC中的多个脚本块共享导入的符号

通配符路径与每条路径一起显示

如何将所有props传递给React中的组件?

如何在已知基类的任何子类上使用`extends`?

基元类型按引用传递的解决方法

如何创建由空格连接的多个字符串的类型

编剧- Select 动态下拉选项

如何在Next.js和Tailwind.css中更改悬停时的字体 colored颜色

VUE 3类型';字符串';不可分配给类型';参考<;字符串&>

两个名称不同的相同打字界面-如何使其干燥/避免重复?

如何在TypeScript中将元组与泛型一起使用?

React-跨组件共享功能的最佳实践

我应该使用服务方法(依赖于可观察的)在组件中进行计算吗?

使用 fp-ts 时如何使用 TypeScript 序列化任务执行?

如何在 TypeScript 类型定义中简化定义长联合类型

是否可以使用元组中对象的键来映射类型