Typescript 注意局部变量can';t返回空值
我有以下结构(为了清晰起见省略了一些代码,例如,Typescript 注意局部变量can';t返回空值,typescript,Typescript,我有以下结构(为了清晰起见省略了一些代码,例如,catch): 从“readline”导入readline; let line:readline.Interface | null=null; 试一试{ line=readline.createInterface({ 输入:process.stdin, 输出:process.stdout, }); const question=async(questionText:string)=> 新承诺( (res)=> void line.question(
catch
):
从“readline”导入readline;
let line:readline.Interface | null=null;
试一试{
line=readline.createInterface({
输入:process.stdin,
输出:process.stdout,
});
const question=async(questionText:string)=>
新承诺(
(res)=>
void line.question(`${questionText}\n`,(答案)=>void res(答案))
//^---对象可能为空
);
}最后{
行?.close();
}
rl
永远不会在剩余代码中关闭,也不会重新分配。对于非空断言来说,这是一个有效点吗?静态分析还不够高级,还没有注意到吗?我是否应该转换为一个可选的链,以防止程序员发生错误的机会微乎其微,即有人稍后重新分配rl
我更喜欢
rl
是const
,只是readline.Interface
没有null
,但由于最后尝试/我认为这是不可能的。我认为正确的方法是立即调用createInterface
,然后将try
/最后
放在后面:
const rl=readline.createInterface({
输入:process.stdin,
输出:process.stdout,
});
试一试{
const question=async(questionText:string)=>newpromise(res=>void rl.question(`${questionText}\n`,answer=>void res(answer));
//其他可能会扔的东西
}最后{
rl.close();
}
Typescript认为,rl
此时可能为空,因为rl
是可变的,并且它可能在初始化和评估承诺之间发生突变
最直接的答案是在try-catch之外初始化rl
。看来,readline.createInterface
不会直接失败,因此:
const rl=readline.createInterface({
输入:process.stdin,
输出:process.stdout,
});
试一试{
//在这里使用rl
}最后{
rl.close();
}
如果readline.createInterface
可以抛出,那么承诺只需要通过const引用访问readline接口值。e、 g
让rl:Readline.Interface | null=null;
试一试{
rl=readline.createInterface({/*…snip*/});
常量readlineInterface=rl;
const question=async(questionText:string):Promise=>
新承诺((决议)=>{
readlineInterface.question(`${questionText}\n`,resolve)
});
}最后{
rl?.close();
}
这个例子有点做作,但很好地说明了这个想法
还考虑将分割初始化和读取分为单独的尝试catch块:
让rl:readline.Interface;
试一试{
rl=readline.createInterface({/*…snip*/});
}捕捉(错误){
//处理错误
}
if(!rl){
//未实例化句柄rl
}
试一试{
//在这里使用rl
}最后{
rl.close();
}
我怀疑静态分析在很大程度上取决于readline.createInterface
返回的类型。如果返回类型包括|null
,则TypeScript无法进行假设,它可能是null
。如果没有静态类型定义,并且默认为any
,那么TypeScript仍然不能保证它不是null
;返回类型不包括| null
,只是为了详细说明:由于rl
用于函数中(闭包超过rl
),因此函数可能在更晚的阶段执行(甚至可以无限期存储)。这意味着,如果以后将rl
重新分配到null
(这是允许的),rl
将突然出现null
。但是,所有代码都在那里,可以进行分析。rl
的任何重新分配在语法上都必须在该函数内部可见。我明白了,静态分析可能还没有到那个地步,我只是想知道,我的想法是否正确,以及如何解决它。readline.createInterface
不会抛出,所以它不需要出现在try
中。但是,您拥有的代码实际上无法捕获任何内容;你必须用async
函数来包装整个过程,然后等待你的承诺(你的async
在这里什么都没有做)。我不知道你的特定问题@Doofus的答案,但绝对不要使用非空断言。它们通常是不好的做法,想象一下,在这种情况下,有人稍后出现并添加了一些代码,允许rl
在调用createInterface
和行之间为空。问题
。您现在已经有了可能被破坏的代码。对于特定的场景,这似乎是最好的方法。我没有想到,<>代码> CureAddioTe界面< /C>不能抛出。如果createInterface
可能抛出,它看起来像是在try
-块内的附加变量中创建副本(它甚至可以是const
,并且在其类型中不会有null
),使用它“尽可能干净”。是的,只要typescript能够确保您正在访问的引用是const并且有一个值,您就可以了。这包括您的建议,通过将let-boundrl
分配给promise内部可以访问的const绑定。我会把它作为我回答的一部分。
import readline from "readline";
let line: readline.Interface | null = null;
try {
line = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = async (questionText: string) =>
new Promise<string>(
(res) =>
void line.question(`${questionText}\n`, (answer) => void res(answer))
// ^--- Object is possibly null
);
} finally {
line?.close();
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
try {
const question = async (questionText: string) => new Promise<string>(res => void rl.question(`${questionText}\n`, answer => void res(answer)));
// other stuff that might throw
} finally {
rl.close();
}