У меня есть следующая конструкция (некоторый код опущен для ясности, например catch):

import readline from 'readline';
let rl: readline.Interface | null = null;
try {
  rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  const question = async (questionText: string) => new Promise<string>(res => void rl.question(`${questionText}\n`, answer => void res(answer)));
  //                             Object is possibly null ----------------------------^
} finally {
  rl?.close();
}

rl никогда не закрывается в оставшемся коде и никогда не переназначается. Это подходящее место для ненулевого утверждения, а статический анализ еще недостаточно продвинут, чтобы его заметить? Должен ли я лучше преобразовать в необязательную цепочку, чтобы предотвратить малейшую вероятность ошибки программиста, когда кто-то переназначит rl позже?

Я бы предпочел, чтобы rl был const и просто readline.Interface без null, но из-за try / finally я думаю, что это невозможно .

0
Doofus 20 Янв 2021 в 21:31

2 ответа

Лучший ответ

Typescript полагает, что rl может иметь значение null в этот момент, потому что rl является изменяемым, и он может быть изменен между инициализацией и оценкой обещания.

Самый простой ответ - инициализировать rl вне try-catch. Не похоже, что readline.createInterface может выйти из строя напрямую, поэтому:

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });

try {
 // use rl here
} finally {
  rl.close();
}
1
Daniel Raloff 20 Янв 2021 в 19:49

Я думаю, что правильный способ сделать это - немедленно вызвать createInterface и поставить try / finally после :

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();
}
0
CertainPerformance 20 Янв 2021 в 18:51