如何使用可覆盖列表类型实现自定义 class?
How can I implement custom class with overridable list type?
我有一个使用 next v10.1.2 和 Typescript v4.4.4 的 Nextjs 项目
我想要一个封装自定义类型列表的 class SortableList
。唯一的要求是列表扩展了我的摘要 class OrderedItem
.
目前,当我尝试访问从新的 SortableList
对象扩展 OrderedItem
的 class 上的 属性 时出现错误。
这在 Typescript 中可行吗?正确的设置方法是什么?
代码:
OrderedItem.ts
// The purpose of this class is to ensure that items in a list can be ordered.
abstract class OrderedItem {
order: number
constructor(order: number) {
this.order = order
}
}
export default OrderedItem
CustomListItem.ts
import OrderedItem from './OrderedItem'
// This is an example of a class that I would like to be orderable.
class CustomListItem extends OrderedItem {
name: string
jsx: JSX.Element
constructor(order: number, name: string, jsx: JSX.Element) {
super(order)
this.name = name
this.jsx = jsx
}
}
export default CustomListItem
SortableList.ts
import OrderedItem from './OrderedItem'
class SortableList {
// I want this array to be overridden by a custom class like CustomListItem
list: OrderedItem[]
constructor(list: OrderedItem[]) {
this.list = list
}
sortByOrder = (): void => {
this.list = this.list.sort((a, b) => a.order - b.order)
}
sortByReverseOrder = (): void => {
this.list = this.list.sort((a, b) => b.order - a.order)
}
}
export default SortableList
example.tsx
import CustomListItem from './CustomListItem'
import SortableList from './SortableList'
export default function Example() {
const customList: CustomListItem[] = [
new CustomListItem(3, 'name3', <div></div>),
new CustomListItem(1, 'name1', <div></div>),
new CustomListItem(2, 'name2', <div></div>),
]
const sortableList: SortableList = new SortableList(customList)
sortableList.sortByOrder()
return sortableList.list.map(item => item.jsx)
}
我从这个例子中得到的错误是在 example.tsx
的 return 语句中尝试访问 item
上的 jsx
属性 因为项目输入为 OrderedItem
而不是 CustomListItem
.
错误:属性 'jsx' 在类型 'OrderedItem' 上不存在。
正确的做法是什么?我试着用 <T>
声明 SortableList
并像
一样初始化它
const sortableList = new SortableList<CustomListItem>(customList)
,但我不熟悉它的工作原理。
问题是当您访问 SortableList
的元素时,编译器只知道它是一个 OrderedItem
,没有 属性 jsx
.
主要有两种解决方法:
- 您知道您的项目将始终属于某种类型(就像您的情况一样)并将其转换为该类型。易于实现,但我真的警告你这不是一个好主意,因为你将打破对象编程原则,而且这可能导致很难调试的运行时错误,例如你放置了一个不是列表中的
CustomListItem
,没有 属性 jsx
。这是如何完成的:
sortableList.list.map(item => (item as CustomListItem).jsx)
- 正式的方式是使用泛型。泛型只是代码中类型的占位符,当您使用它时,它将被指定的类型替换。在您的情况下,您还需要对该泛型进行约束,强制实现指定的类型扩展您的
OrderedItem
class。通过这种方式,您不会遇到意外错误的实际情况,因为 ts 编译器通过检查您的类型来阻止它们。这就是你的情况:
SortableList.ts
import OrderedItem from './OrderedItem'
// T is the generic
// T extends OrderedItem is the constraint
class SortableList<T extends OrderedItem> {
// this array can contains only elements
// that are descendant of T
// T is always a descendant of OrderedItem
list: T[]
constructor(list: T[]) {
this.list = list
}
sortByOrder = (): void => {
this.list = this.list.sort((a, b) => a.order - b.order)
}
sortByReverseOrder = (): void => {
this.list = this.list.sort((a, b) => b.order - a.order)
}
}
export default SortableList
example.tsx
import CustomListItem from './CustomListItem'
import SortableList from './SortableList'
export default function Example() {
const customList: CustomListItem[] = [
new CustomListItem(3, 'name3', <div></div>),
new CustomListItem(1, 'name1', <div></div>),
new CustomListItem(2, 'name2', <div></div>),
]
const sortableList: SortableList<CustomListItem> =
new SortableList<CustomListItem>(customList)
sortableList.sortByOrder()
return sortableList.list.map(item => item.jsx)
}
我有一个使用 next v10.1.2 和 Typescript v4.4.4 的 Nextjs 项目
我想要一个封装自定义类型列表的 class SortableList
。唯一的要求是列表扩展了我的摘要 class OrderedItem
.
目前,当我尝试访问从新的 SortableList
对象扩展 OrderedItem
的 class 上的 属性 时出现错误。
这在 Typescript 中可行吗?正确的设置方法是什么?
代码:
OrderedItem.ts
// The purpose of this class is to ensure that items in a list can be ordered.
abstract class OrderedItem {
order: number
constructor(order: number) {
this.order = order
}
}
export default OrderedItem
CustomListItem.ts
import OrderedItem from './OrderedItem'
// This is an example of a class that I would like to be orderable.
class CustomListItem extends OrderedItem {
name: string
jsx: JSX.Element
constructor(order: number, name: string, jsx: JSX.Element) {
super(order)
this.name = name
this.jsx = jsx
}
}
export default CustomListItem
SortableList.ts
import OrderedItem from './OrderedItem'
class SortableList {
// I want this array to be overridden by a custom class like CustomListItem
list: OrderedItem[]
constructor(list: OrderedItem[]) {
this.list = list
}
sortByOrder = (): void => {
this.list = this.list.sort((a, b) => a.order - b.order)
}
sortByReverseOrder = (): void => {
this.list = this.list.sort((a, b) => b.order - a.order)
}
}
export default SortableList
example.tsx
import CustomListItem from './CustomListItem'
import SortableList from './SortableList'
export default function Example() {
const customList: CustomListItem[] = [
new CustomListItem(3, 'name3', <div></div>),
new CustomListItem(1, 'name1', <div></div>),
new CustomListItem(2, 'name2', <div></div>),
]
const sortableList: SortableList = new SortableList(customList)
sortableList.sortByOrder()
return sortableList.list.map(item => item.jsx)
}
我从这个例子中得到的错误是在 example.tsx
的 return 语句中尝试访问 item
上的 jsx
属性 因为项目输入为 OrderedItem
而不是 CustomListItem
.
错误:属性 'jsx' 在类型 'OrderedItem' 上不存在。
正确的做法是什么?我试着用 <T>
声明 SortableList
并像
const sortableList = new SortableList<CustomListItem>(customList)
,但我不熟悉它的工作原理。
问题是当您访问 SortableList
的元素时,编译器只知道它是一个 OrderedItem
,没有 属性 jsx
.
主要有两种解决方法:
- 您知道您的项目将始终属于某种类型(就像您的情况一样)并将其转换为该类型。易于实现,但我真的警告你这不是一个好主意,因为你将打破对象编程原则,而且这可能导致很难调试的运行时错误,例如你放置了一个不是列表中的
CustomListItem
,没有 属性jsx
。这是如何完成的:
sortableList.list.map(item => (item as CustomListItem).jsx)
- 正式的方式是使用泛型。泛型只是代码中类型的占位符,当您使用它时,它将被指定的类型替换。在您的情况下,您还需要对该泛型进行约束,强制实现指定的类型扩展您的
OrderedItem
class。通过这种方式,您不会遇到意外错误的实际情况,因为 ts 编译器通过检查您的类型来阻止它们。这就是你的情况:
SortableList.ts
import OrderedItem from './OrderedItem'
// T is the generic
// T extends OrderedItem is the constraint
class SortableList<T extends OrderedItem> {
// this array can contains only elements
// that are descendant of T
// T is always a descendant of OrderedItem
list: T[]
constructor(list: T[]) {
this.list = list
}
sortByOrder = (): void => {
this.list = this.list.sort((a, b) => a.order - b.order)
}
sortByReverseOrder = (): void => {
this.list = this.list.sort((a, b) => b.order - a.order)
}
}
export default SortableList
example.tsx
import CustomListItem from './CustomListItem'
import SortableList from './SortableList'
export default function Example() {
const customList: CustomListItem[] = [
new CustomListItem(3, 'name3', <div></div>),
new CustomListItem(1, 'name1', <div></div>),
new CustomListItem(2, 'name2', <div></div>),
]
const sortableList: SortableList<CustomListItem> =
new SortableList<CustomListItem>(customList)
sortableList.sortByOrder()
return sortableList.list.map(item => item.jsx)
}