typescript - 如何防止使用其中一种类型的可选属性时出错
问题描述
Typescript linter 希望查看由 .children
返回的所有类型的属性transform
。当我已经写支票时如何解决它prop || []
interface NestedItem{
children?: NestedItem[]
}
function buildTree<Item, TransformedItem extends NestedItem>(
data: Item[],
transform: (item: Item) => TransformedItem | Item = item => item
) {
const result = []
for (const item of data) {
const transformedItem = transform({...item})
const resultItem = Object.assign({}, transformedItem, {
children: transformedItem.children || [] // TS2339: Property 'children' does not exist on type 'Item | TransformedItem'. Property 'children' does not exist on type 'Item'.
})
result.push(resultItem)
}
return result
}
解决方案
编译器不知道是否Item
会有children
属性。一般来说,TypeScript 对象类型是可扩展的/开放的,而不是精确的/封闭的(如microsoft/TypeScript#12936 中);如果对象类型的定义中没有提到某个属性,这并不意味着该类型的值将缺少该键的属性;这意味着编译器不知道那个键是什么(如果有的话)。由于 for 的类型声明Item
没有提及children
属性的特定类型,因此编译器将其视为可能存在,类型为unknown
or any
,并且对该属性的任何访问都被视为错误。
因此,工会Item | NestedItem
也有同样的问题;因为它可能是一个Item
并且我们不能仅仅访问children
它。
解决此问题的一种方法是进行约束 Item
,以便如果它确实具有children
属性,它将是undefined
:
function buildTree<
Item extends { children?: undefined },
TransformedItem extends NestedItem
>(
data: Item[],
transform: (item: Item) => TransformedItem | Item = item => item
) {
const result = []
for (const item of data) {
const transformedItem = transform({ ...item })
const resultItem = Object.assign({}, transformedItem, {
children: transformedItem.children || [] // no error
})
result.push(resultItem)
}
return result
}
这解决了实现中的问题,尽管它可能更难调用buildTree()
,因为您必须保证data
元素必然缺少children
属性......正如我所提到的,这不是它的工作方式:
interface Foo {
a: string;
}
const foos: Foo[] = [{ a: "A" }, { a: "B" }];
buildTree(foos, x => x) // error!
// -----> ~~~~
// type 'Foo[]' is not assignable to type '{ children?: undefined; }[]
发生该错误是因为编译器无法保证 aFoo
没有children
属性。从技术上讲,这是事实:
const hmm = { a: "C", children: "no thanks" };
foos.push(hmm);
因此,此版本为您提供最大的类型安全性,但牺牲了便利性。
如果您不担心data
' 的元素可能具有某些children
属性的可能性,那么您可以在 的实现中使用类型断言buildTree()
来告诉编译器transformedItem
' 的children
属性要么丢失要么NestedItem
:
function buildTree<Item, TransformedItem extends NestedItem>(
data: Item[],
transform: (item: Item) => TransformedItem | Item = item => item
) {
const result = []
for (const item of data) {
const transformedItem = transform({ ...item })
const resultItem = Object.assign({}, transformedItem, {
children: (transformedItem as TransformedItem |
{ children?: never }).children || []
})
result.push(resultItem)
}
return result
}
buildTree()
因此,如果您调用a ,您将不会收到错误Foo[]
:
const works = buildTree(foos, x => x); // okay
但是请注意,如果您在数组元素中确实有一个意外children
属性,它可能会在运行时导致未捕获的错误,因为您对编译器撒了谎:
for (const w of works) {
console.log(w.children.join(","));
// "", "", and then RUNTIME ERROR! w.children.join is not a function
}
推荐阅读
- jsdoc - JsDoc:在函数描述中提到一个参数
- html - 将 flex 包裹的元素与容器底部对齐?
- javascript - React Js axios设置状态和自我不起作用
- jira - 获取与 Jira 相同格式的 PocketQuery 结果
- angular - Angular生命周期事件调用而不实现接口?
- php - 使用 PHP 将时间保存为数据库中的时间戳
- python - 文件读取连接
- javascript - 用 php/mysql 中的时间段进行说明
- javascript - 在Javascript中循环JSON对象,得到错误的结果
- python - 如何使用 python 请求设置内容类型的边界?