Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 注意局部变量can';t返回空值_Typescript - Fatal编程技术网

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-bound
rl
分配给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();
}