TypeScript:描述对象的对象

Dus*_*els 3 javascript typescript

说我有一个像这样的界面:

interface Student {
  firstName: string;
  lastName: string;
  year: number;
  id: number;
}
Run Code Online (Sandbox Code Playgroud)

如果我想传递这些对象的数组,可以简单地将类型写为Student[]

为了方便查找,我使用的是一个对象,其中学生ID是键,而学生是值,而不是数组。

let student1: Student;
let student2: Student;
let students = {001: student1, 002: student2 }
Run Code Online (Sandbox Code Playgroud)

有什么方法可以将此数据结构描述为我要传入函数或从函数返回的类型?

我可以这样定义一个接口:

interface StudentRecord {
  id: number;
  student: Student
}
Run Code Online (Sandbox Code Playgroud)

但这仍然不是我想要的类型。我需要指出我有一个满是这样的对象的对象,同样的方式也Student[]表明我有满是这样的对象的数组。

Kar*_*ski 8

使用内置Record类型:

type StudentsById = Record<Student['id'], Student>;
Run Code Online (Sandbox Code Playgroud)

  • `Record` 知道你的 `keyofStringsOnly` 设置。如果它被打开,它将只允许类型为 `string` 的键(这是 JavaScript 在运行时对键所做的)。 (3认同)

mes*_*ill 6

您可以简单地使密钥动态化:

interface IStudentRecord {
   [key: string]: Student
}
Run Code Online (Sandbox Code Playgroud)

  • messerbill 所说的“动态”通常称为索引签名(https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures)。 (2认同)

Ret*_*sam 6

有几种方法可以解决这个问题:

字符串索引签名

正如@messerbill 的回答所提到的,您可以为此使用索引签名:

interface StudentRecord {
    [P: string]: Student;
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您想更加谨慎:(请参阅下面的警告)

interface StudentRecordSafe {
    [P: string]: Student | undefined;
}
Run Code Online (Sandbox Code Playgroud)

映射类型语法

或者,您也可以使用映射类型语法:

type StudentRecord = {
    [P in string]: Student;
}
Run Code Online (Sandbox Code Playgroud)

或更谨慎的版本:(见下面的警告)

type StudentRecordSafe = {
    [P in String]?: Student
}
Run Code Online (Sandbox Code Playgroud)

它与字符串索引签名非常相似,但可以使用其他东西代替string,例如特定字符串的联合。还有一个实用程序类型,Record它定义为:

type Record<K extends string, T> = {
    [P in K]: T;
}
Run Code Online (Sandbox Code Playgroud)

这意味着你也可以把它写成type StudentRecord = Record<string, Student>. (或type StudentRecordSafe = Partial<Record<string, Student>>)(这是我通常的偏好,因为它是 IMO,比长手索引或类型映射语法更易于读写 Record)

带有索引签名和映射类型语法的警告

对这两者的警告是,他们对给定 id 的学生的存在“持乐观态度”。他们假设对于任何字符串键,都有一个相应的Student对象,即使情况并非如此:例如,这对两者都编译:

const students: StudentRecord = {};
students["badId"].id // Runtime error: cannot read property id of undefind
Run Code Online (Sandbox Code Playgroud)

使用相应的“谨慎”版本:

const students: StudentRecordSafe = {}
students["badId"].id;  // Compile error, object is potentially undefined
Run Code Online (Sandbox Code Playgroud)

这是一个有点更恼人的使用,特别是如果你知道你将只查找存在的ID,但它肯定键入更安全。

从 4.1 版开始, Typescript 现在有一个称为noUncheckedIndexedAccess修复此问题的标志- 现在,当启用该标志时,对这样的索引签名的任何访问现在都将被视为可能未定义。如果标志打开,这使得“谨慎”版本变得不必要。(该标志不会自动包含strict: true在 tsconfig 中,必须直接在 tsconfig 中启用)

地图对象

代码略有变化,但Map也可以使用适当的对象,而且它始终是“安全”版本,您必须在其中正确检查该对象`

type StudentMap = Map<string, Student>;
const students: StudentMap = new Map();
students.get("badId").id; // Compiler error, object might be undefined
Run Code Online (Sandbox Code Playgroud)