typescript - 以嵌套方式将界面中的所有字段设为只读

标签 typescript nested readonly readonly-attribute

我知道,我们可以使用 Readonly< T>T 的所有字段重新声明为只读,如:[ Typescript: extending an interface and redeclaring the existing fields as readonly

嵌套字段呢?例如:

interface School {
  teachers: Array<Teacher>;
  students: Array<Student>;
}

interface Teacher {
  teacherId: number;
  personInfo: PersonInfo;
}

interface Student {
  studentId: number;
  personInfo: PersonInfo;
}

interface PersonInfo {
  name: string;
  age: number
}

如何创建一个 SchoolReadonly 类型,其中所有嵌套字段都是只读的。

一个简单的测试用例:

var s: SchoolReadonly = {
  teachers: [{teacherId: 1, personInfo: {name: "John", age: 40}}],
  students: [{studentId: 1, personInfo: {name: "Dan", age: 20}}]
}

s.teachers[0].personInfo.name = "John2"; //should produce readonly error
s.students[0].personInfo.age = 22; //should produce readonly error

作为约束,我不想将 Readonly 直接添加到 School、Teacher、Student 和 PersonInfo 接口(interface)。

最佳答案

您可以使用泛型和类型别名,这是很多代码,但它可以解决问题:

interface School<T extends Teacher<PersonInfo>, S extends Student<PersonInfo>> {
    teachers: Array<T>;
    students: Array<S>;
}

type EditableSchool = School<EditableTeacher, EditableStudent>;
type ReadonlySchool = Readonly<School<ReadonlyTeacher, ReadonlyStudent>>;

interface Teacher<P extends PersonInfo> {
    teacherId: number;
    personInfo: P;
}

type EditableTeacher = Teacher<PersonInfo>;
type ReadonlyTeacher = Readonly<Teacher<Readonly<PersonInfo>>>;

interface Student<P extends PersonInfo> {
    studentId: number;
    personInfo: P;
}

type EditableStudent = Student<PersonInfo>;
type ReadonlyStudent = Readonly<Student<Readonly<PersonInfo>>>;

interface PersonInfo {
    name: string;
    age: number
}

var s: ReadonlySchool = {
  teachers: [{teacherId: 1, personInfo: {name: "John", age: 40}}],
  students: [{studentId: 1, personInfo: {name: "Dan", age: 20}}]
}

s.teachers = null;
s.teachers[0].personInfo.name = "John2"; // Cannot assign to 'name' because it is a constant or a read-only property
s.students[0].personInfo.age = 22; // Cannot assign to 'age' because it is a constant or a read-only property

( code in playground )


编辑

如果您希望 s.teachers[0] = nul 失败,那么您还需要将 Array 更改为 ReadonlyArray,因此:

interface School<T extends Teacher<PersonInfo>, Ta extends ArrayLike<T>, S extends Student<PersonInfo>, Ts extends ArrayLike<S>> {
    teachers: Ta;
    students: Ts;
}

type EditableSchool = School<EditableTeacher, Array<EditableTeacher>, EditableStudent, Array<EditableStudent>>;
type ReadonlySchool = Readonly<School<ReadonlyTeacher, ReadonlyArray<ReadonlyTeacher>, ReadonlyStudent, ReadonlyArray<ReadonlyStudent>>>;

其余的是一样的,但是:

s.teachers = null; // Cannot assign to 'teachers' because it is a constant or a read-only property
s.teachers[0] = null; // Index signature in type 'ReadonlyArray<Readonly<Teacher<Readonly<PersonInfo>>>>' only permits reading

诚然,这不是一个简单的解决方案,而且非常冗长,但我不认为存在针对该问题的简单通用解决方案,至少目前如此。
考虑:

type ReadyonlyDeep<T> = {
    readonly [P in keyof T]: ReadyonlyDeep<T[P]>;
}

只要对象只有其他简单对象,这就很好用:

interface A {
    b: B
}

interface B {
    str: string;
}

let a: ReadyonlyDeep<A>;
a.b.str = "fe"; // Cannot assign to 'str' because it is a constant or a read-only property

但是如果你引入一个数组:

interface B {
    str: string;
    moreB: B[];
}

然后:

a.b.moreB = []; // Cannot assign to 'moreB' because it is a constant or a read-only property
a.b.moreB[0].str = "hey"; // this is fine though

这可能不是一个容易解决的问题。

关于typescript - 以嵌套方式将界面中的所有字段设为只读,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41372138/

相关文章:

javascript - 如果我使用 "function"kw 是否可以使 TS 抛出错误?

arrays - Elasticsearch - 过滤其中(嵌套数组之一)和(所有嵌套数组)

c# - 是否可以返回调用者无法修改的只读 ObservableCollection?

typescript - vue3 vite 别名无法按预期使用 typescript

angularjs - 如何将我的 javascript AngularJS 应用程序 Controller 转换为使用 Typescript?

javascript - 等待带有定位器的元素时超时

css - 如何最好地处理 html5 中嵌套 h1 的样式?

javascript - 删除嵌套数组中不匹配的对象

html - knockout attr 与 'readonly' 和 'disabled' 等属性的绑定(bind)

java - 在java中编辑只读文件