以嵌套方式将界面中的所有字段设为只读
Make all fields in an interface readonly in a nested way
我知道,我们可以使用 Readonly< T> 将 T 的所有字段重新声明为只读,如:[
嵌套字段呢?例如:
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 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
编辑
如果您希望 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
这可能不是一个容易解决的问题。
我知道,我们可以使用 Readonly< T> 将 T 的所有字段重新声明为只读,如:[
嵌套字段呢?例如:
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 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
编辑
如果您希望 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
这可能不是一个容易解决的问题。