我下面的代码片段是有可能注释readText
来告诉它保证了编译器this.text
是string
不是string | undefined
?
type MyResponse = {
text: () => Promise<string>;
};
class ResponseVerfier {
response: MyResponse;
text?: string;
constructor(response: MyResponse) {
this.response = response;
}
async readText() {
this.text = await this.response.text();
}
async verifyTextContains(value: string) {
await this.readText();
// I've run this.readText so I now know this.text is a string, not string | undefined
// However I'm currently getting an error "Object is possibly 'undefined'"
return this.text.includes(value);
}
}
I think TypeScript is missing several features that you'd need to get this code to work so that the runtime code is the same and the compiler verifies its type safety. The first missing feature is some way to do user-defined type assertions (as suggested in this GitHub issue) in a way similar to how user-defined type guards work already. That way you could tell the compiler that this.readText()
will (eventually) narrow the type of this
to this & {text: string}
. Even with that feature, you'd need to be able to return a Promise
of the type assertion instead of the type assertion itself, which would require some thing like propagation of type predicates (as suggested in this GitHub issue). If both of those were implemented, then maybe you could do something like:
// DOES NOT COMPILE, DON'T TRY THIS
async readText(): Promise<this as (this & {text: string})> {
this.text = await this.response.text();
}
其中,Promise<this as (this & {text: string})>
该方法readText()
返回一个承诺是,解决的情况下,缩小的类型this
的东西,其text
性能绝对是一个string
不只是string | undefined
。las,这行不通。
使代码按原样工作而不在运行时进行任何更改的方法是使用类型声明,例如@Phillip的answer中所示的非null声明!
运算符。
如果您不介意更改代码的工作方式,那么我强烈建议将异步和同步代码分成两个完全不同的结构,并让异步代码返回完全配置的同步对象,如下所示:
type MyResponse = {
text: () => Promise<string>;
};
// all async code should be in here
class AsyncResponseVerifier {
constructor(public response: MyResponse) {}
async readText(): Promise<SyncResponseVerifier> {
return new SyncResponseVerifier(await this.response.text());
}
}
// all code in here should be synchronous
class SyncResponseVerifier {
constructor(public text: string) {}
verifyTextContains(value: string) {
return this.text.includes(value);
}
}
并且您将像这样使用它:
// use the async thing to get the sync thing
async function doThings() {
const asyncRV = new AsyncResponseVerifier({
text: async () =>
"an important forum raising awareness about serious things"
});
const syncRV = await asyncRV.readText();
syncRV.verifyTextContains("rum raisin");
}
否则,您将获得一个分裂性格的班级,在该班级中应该await
编辑某些内容,而其他则不应该。即使您能弄清楚如何使编译器跟踪哪个是哪个(并且正如我在顶部所说的那样,我认为您也无法做到),开发人员可能很难跟踪。
无论如何,希望能有所帮助。祝好运!