Проблема здесь в назначении:
const value: string | null = 0 as any
Компилятор знает, что value
является константой, поэтому он никогда не может быть нулевым.
Не станет намного лучше, если вы используете let
:
let value: string | null = 'foo';
if (isNotNull(value)) {
// `value` is of type `string` here
} else {
// `value` is of type `never` here
}
Опять же, компилятор машинописного текста знает, что значение не равно нулю.
Но если назначить что-то, где Typescript не может вывести постоянное значение, то у вас будет ожидаемый string
в if
ветви и null
в else
:
function isNotNull<T> (arg: T): arg is Exclude<T, null> {
return arg !== null
}
let value: string | null = foo();
if (isNotNull(value)) {
// `value` is of type `string` here
} else {
// `value` is of type `null` here
}
function foo(): string | null {
return "foo"
}
Кроме того, даже этот последний пример работает, только если для параметра компилятора strictNullChecks
установлено значение true. Если вы не включили strictNullChecks
, вы не можете исключить null
из type
, и ветвь if
будет string
, что совпадает с string | null
, так что для else
остается только never
.
Edit:
Причина, по которой это не работает, когда strictNullChecks
выключен, довольно интересна. В этом случае типы string | null
и string
идентичны. Это означает, что value
на самом деле имеет тип string
, а Exclude<string, null>
просто string
(и, следовательно, по-прежнему включает null
), поэтому предложение else
осталось с типом never
для value
.