>我有一个带有可选ID的书的界面(例如,一本尚未保存到服务器的新书(:
interface Book {
id?: string
}
当我从后端获取所有书籍时 - 我用来创建一个对象 byId:
allIds: payload.map(book => book.id!),
其中allIds
声明是:
allIds: string[]
如您所见,我在book.id
后添加了!
,以指定每本书都有一个 Id。 好像我不会那样做,打字稿不会编译:
TS2322:类型"(字符串|未定义([]"不能分配给类型"字符串[]"。
类型"字符串|未定义"不能分配给类型"字符串"。
类型">未定义"不可分配给类型"字符串"。
现在,它正在与!
一起工作,我不希望任何一本书缺少它的id,但我想找到一种优雅的方式来验证它......以防万一。
我可以做这样的事情:
allIds: payload.map(book => {
if (!book.id) {
throw new Error("invalid book id")
}
return book.id
}),
但我想知道您是否对更好的单行优雅解决方案有任何想法,以便在映射时验证项目。
我不希望任何一本书缺少它的id
然后从界面中删除?
。
如果你想过滤掉没有id
的元素allIds
那么所有的字符串,如果你坚持单行:
const allStrings: string[] = payload.map(book => book.id).filter(<T>(v: T): v is Exclude<T, undefined> => typeof v !== 'undefined');
如果由于某种原因您无权访问Book
接口,则可以将此类型用于传入数据:
Required<Book>
这将使id
不再是可选的。
如何将断言逻辑封装在一个单独的函数中?
function assertDefined<T>(t: T | undefined | null): T {
if (t == undefined) throw new Error("undefined or null item.")
return t
}
const res = payload.map(book => assertDefined(book.id)) // string[]
您可以定义一个propOrThrow
函数以使其更短一些:
function propOrThrow<T, U>(mapper: (t: T) => U | undefined) {
return (t: T): U => assertDefined(mapper(t))
}
const res2 = payload.map(propOrThrow(b => b.id)) // string[]
代码示例