typescript - 如何在 TypeScript 中将类型参数设置为“无”?
问题描述
在我们的应用程序中,我们有一个数据“包装器”模式,我们使用一个类的单个实例来表示一些可能尚未加载或尚未加载的数据。像这样的东西:
abstract class BaseWrapper<TData> {
protected data?: TData;
loadedPromise: Promise<this>;
}
然后,您将为特定的数据实现此功能,如下所示:
interface PersonData {
name: string;
}
class PersonWrapper extends BaseWrapper<PersonData> {
get name() { return this.data && this.data.name }
}
给定一个人:
const person: PersonWrapper = ...;
person.name
is string | undefined
since的类型this.data
可能未定义。这很好,因为它提醒我们对象可能没有被加载,我们需要在访问它的成员时处理这种情况。
但是,在某些情况下,我们知道对象已加载,最常见的loadedPromise
是在完成时,如下所示:
(await person.loadedPromise).name
string | undefined
即使我们知道对象已加载,该表达式的类型仍然是。这很烦人,我想帮助解决这个问题。
我最好的想法是BaseWrapper
使用“空”类型参数进行参数化。像这样的东西:
abstract class BaseWrapper<TData, TEmpty = undefined> {
protected _data?: TData;
get data(): TData | TEmpty {
return this._data as (TData | TEmpty);
}
}
然后,我们可以这样做:
class PersonWrapper<TEmpty = undefined> extends BaseWrapper<PersonData, TEmpty> {
get name() { return this.data && this.data.name }
loadedPromise: Promise<PersonWrapper<nothing>>;
requireLoaded(): PersonWrapper<nothing> {
if (this.data) return this as PersonWrapper<nothing>
else throw new Error("Not loaded!")
}
}
这里的 issuer 是“nothing”类型。我希望能够将类型传递给参数,使得TData | TEmpty
等于TData
. 这种类型应该是什么?
假设这是可能的,那么类型(await person.loadedPromise).name
就是string
,这太棒了。
有任何想法吗?
解决方案
一种可能的解决方案是使用!
后缀,它可以确保编译器有一个可选值可用:
const name: string = (await person.loadedPromise).name!
这适用于程序员“知道”该值已加载的非常明显的情况。
(嘿,我在重复你的“TData | TEmpty
等于TData
”的想法。你想要一种安全透明地封装可能的空虚的类型,即使你知道值已定义,也不需要你处理未定义的情况......)
另一个想法是:与其拥有name
be的类型,string | undefined
为什么不拥有它... string[]
!哈哈哈,忍我!
type Maybe<T> = T[] // I know, I know... this is weird
function of <T>(value: T): Maybe<T> {
return value === undefined ? [] : [value]
}
abstract class BaseWrapper<TData> {
protected data?: TData;
loadedPromise: Promise<this>;
}
interface PersonData {
name: string;
}
class PersonWrapper extends BaseWrapper<PersonData> {
get name() { return of(this.data && this.data.name) }
}
const person: PersonWrapper = ...;
// these typings will be inferred I'm just putting them here to
// emphasize the fact that these will both have the same type
const unloadedName: Maybe<string> = person.name
const loadedName: Maybe<string> = (await person.loadedPromise).name
现在,如果您想使用这些名称中的任何一个...
// reduce will either return just the name, or the default value
renderUI(unloadedName.reduce(name => name, 'defaultValue')
renderUI(loadedName.reduce(name => name, 'defaultValue')
该Maybe
类型表示一个可能未定义的值,并且它具有一个安全且一致的接口来访问它,而不管其状态如何。reduce 的默认值不是可选参数,所以不会忘记它:你总是要处理 undefined 的情况,这是非常透明的!如果您想进行更多处理,可以使用map
.
unloadedName
.map(name => name.toUpperCase)
.map(name => leftPad(name, 3))
.map(name => split('')) // <-- this actually changes the type
.reduce(names => names.join('_'), 'W_A_T') // <-- now it's an array!
这意味着您可以编写如下函数,
function doStuffWithName(name: string) {
// TODO implement this
return name
}
并像这样称呼他们:
const name = person.name
.map(doStuffWithName)
.reduce(name => name, 'default')
renderUI(name)
并且您的辅助函数不需要知道它们正在处理可选值。由于 map 的工作方式,如果缺少该值,则name
will[]
和 map 将应用给定函数 0 次......即。安全!
如果你对像这样重载数组类型感到不舒服,你总是可以Maybe
作为一个类来实现:它只需要map
and reduce
。你甚至可以在你的包装类上实现map
和reduce
(因为 Maybe 只是可能未定义数据的包装)。
这不是具有“无类型”的“特定于打字稿”的解决方案,但也许它会帮助您找到解决方案!
(注意:这很像暴露潜在的承诺,我认为这也是你可能要考虑的事情)
推荐阅读
- javascript - 如何将数据从 app.component 传递给 ionic 中的子组件或其他组件
- javascript - 如何复制特定的 imdb ID 并将其添加到另一个状态?
- c++ - 下面的代码是否可能存在内存泄漏?
- python-3.x - TypeError: int() 参数必须是字符串、类似字节的对象或数字,而不是“列表”(Python3.6)
- javascript - Math.pow 在 calc 中返回 0
- android - 如何在 ionic 2 中实现 In App Purcharse?
- javascript - For循环和2个数组
- spring - Spring:如何从数据库中显示百里香中的图像
- php - 调用未定义的方法 Illuminate\Database\Query\Builder::appends() 错误
- javascript - 无法 console.log 获取结果