Как подавить «ошибку TS2533: объект может быть« нулевым »или« неопределенным »»?


226

У меня есть type:

type tSelectProtected = {
  handleSelector?: string,
  data?: tSelectDataItem[],

  wrapperEle?: HTMLElement,
  inputEle?: HTMLElement,
  listEle?: HTMLElement,
  resultEle?: HTMLElement,

  maxVisibleListItems?: number
}

Я объявляю глобальную модульную переменную:

var $protected : tSelectProtected = {};

Я назначаю правильное значение в function1()области видимости:

$protected.listEle = document.createElement('DIV');

Позже по function2()объему звоню:

$protected.listEle.classList.add('visible');

Я получаю ошибку TypeScript:

error TS2533: Object is possibly 'null' or 'undefined'

Я знаю, что могу сделать явную проверку, используя if ($protected.listEle) {$protected.listEle}для успокоения компилятор, но это кажется очень неудобным для большинства нетривиальных случаев.

Как эта ситуация может или должна быть обработана без отключения проверки компилятора TS?

Ответы:


99

Эта функция называется «строгие проверки на ноль», чтобы ее отключить, убедитесь, что --strictNullChecksфлаг компилятора не установлен.

Тем не менее, существование nullуже было описано , как миллиард долларов Mistake , так что приятно видеть такие языки, как машинопись введения затруднительной. Я настоятельно рекомендую держать его включенным.

Один из способов исправить это - убедиться, что значения никогда не заданы nullили undefined, например, инициализировать их заранее:

interface SelectProtected {
    readonly wrapperElement: HTMLDivElement;
    readonly inputElement: HTMLInputElement;
}

const selectProtected: SelectProtected = {
    wrapperElement: document.createElement("div"),
    inputElement: document.createElement("input")
};

Смотрите ответ Райана Кавано для альтернативного варианта, хотя!


8
Лично я использую nulls в «ванильном» JavaScript для инициализации переменных или значений свойств. Это дает мне прямой ответ, если данный var или prop существует, но у него еще нет «полезного значения» или «значение было очищено в некоторой точке выполнения». Это просто условно. Это может быть не лучшим подходом в TypeScript, как я вижу по ответам здесь. Спасибо за ваши мысли.
Грасналь

26
Даже инициализация не подавляет для меня «Объект, возможно,« неопределен »» в TS 2.7.2
Tyguy7

1
Да, но эти определения изменяют значения объектов, например, HTMLDivElement не имеет ближайшего целевого объекта и других событий и свойств базового элемента.
Кларенс

5
Что, если вы пытаетесь описать состояние некоторого свойства объекта Javascript, где истинное представление начального состояния null?
Тимур Мамедов

1
Добавим, что !доступен оператор, очень похожий на операторы в Kotlin, который позволяет вам сделать эти проверки намного более краткими
Alvaro

602

Если вы знаете из внешних средств, что выражение не равно nullили undefined, вы можете использовать оператор ненулевого утверждения, !чтобы принудительно вызвать эти типы:

// Error, some.expr may be null or undefined
let x = some.expr.thing;
// OK
let y = some.expr!.thing;

12
Спасибо, что сообщили мне об ! - Non-null assertion operatorоператоре. Кажется, что эта вещь еще недостаточно хорошо документирована ( https://github.com/Microsoft/TypeScript/issues/11494 ), поэтому любой, кто ищет ответы, прочтет этот http://stackoverflow.com/questions/38874928/operator-in-typescript -after-object-method
grasnal

9
это также не влияет на меня на tsc v2.7.2
Tyguy7

6
@ThomasSauvajon !оператор здесь не делает то же самое, что ?в C # делает! Это только утверждение системы типов ; это не приведет к сбою вашей программы при попытке прочитать свойство из nullили undefined.
Райан Кавано

2
Из справочника, добавленного по указанию Райана: Синтаксис postfix !: identifier!удаляет nullи undefinedиз типаidentifier Это то, что Райан уже сказал, но я тоже считаю этот способ полезным.
Джон Хаттон

6
Это не работает в Typescript 3.2.2. Это было удалено?
Ларс Нистрем

25

Я использовал:

if (object !== undefined) {
    // continue - error suppressed when used in this way.
}

В качестве альтернативы вы можете использовать приведение типа:

const objectX = object as string

Хотя, прежде чем выбрать один из вышеперечисленных обходных путей, рассмотрите архитектуру, к которой вы стремитесь, и это влияет на общую картину.


57
по какой-то причине мой TSC игнорирует оператор if, но все еще считает его неопределенным ...
Tyguy7

9
моя ошибка НЕ ​​подавляется при использованииif(object!==undefined) object.function();
Jérémy

Можно также использовать двойное сравнение с nullи undefined, и это не плохая практика (только если используется с этими двумя типами) - событие TSLint позволит вам это сделать. Это облегчает проверку, если что-то определено, потому что вместо записи null !== someObject && undefined !== someObjectвы можете использовать простоnull != someObject
Marecky

15

Не прямой ответ на вопрос ОП, но в моем случае у меня была следующая настройка -

Машинопись - v3.6.2
tslint -v5.20.0

И используя следующий код

const refToElement = useRef(null);

if (refToElement && refToElement.current) {
     refToElement.current.focus(); // Object is possibly 'null' (for refToElement.current)
}

Я пошел дальше, подавив компилятор для этой строки. Обратите внимание, что, поскольку это ошибка компилятора, а не ошибки линтера, // tslint:disable-next-lineона не работает. Кроме того, согласно документации, это должно использоваться редко, только когда это необходимо -

const refToElement = useRef(null);

if (refToElement && refToElement.current) {
     // @ts-ignore: Object is possibly 'null'.
     refToElement.current.focus(); 
}

ОБНОВЛЕНИЕ :

С Typescript 3.7 вы можете использовать необязательную цепочку , чтобы решить вышеуказанную проблему как -

refToElement?.current?.focus();

2
Гораздо более подходящее решение, чем отключение strictNullChecks, следует использовать с осторожностью, в большинстве случаев вам нужна нулевая ссылка на ошибку, потому что она может вызвать реальные головные боли в будущем.
Энди

Свойство 'getBoundingClientRect' не существует для типа 'никогда'.
mqliutie

В моем случае это необязательное сцепление не сработало. const input = useRef<HTMLInputElement>(null);и if (input && input.current) { input.current.value = ''; }сделал свое дело.
Тимо

13

Это решение сработало для меня:

  • перейдите в tsconfig.json и добавьте "strictNullChecks": false

введите описание изображения здесь


Это сработало и для меня. Несмотря на то, что он по-прежнему выдает ошибку, как, например, в операторах подписки, он не распознает переменную результата, машинопись хочет, чтобы она объявила .subscribe (result => this.result = result.json ());
Аарчи

Вы пытались использовать оператор «карта»? Google 'rxjs / map'. Я в основном делаю: Http.get (...). Map (result => result.json ()). Подписаться (result => {делайте ваши вещи здесь})
Махеш Непал

38
не отвечает на вопрос. OP прямо сказал: «без отключения проверки компилятора TS»
pvill

4
Какой смысл использовать TypeScript и устранять ошибку линтера, чтобы предупредить вас? Я думаю, что лучший вариант - использовать желаемое значение, asесли вы уверены на 100%. Я получил случай с возвращаемым значением mongodb и FindOneOrUpdate, и мне пришлось преобразовать его в схему, потому что result.valueон объявлен как, TSchema | undefinedи я уже проверял это result.okраньше
Vincent

12

Если вы знаете, что тип никогда не будет nullили undefined, вы должны объявить его как foo: Barбез ?. Объявление типа с ? Barсинтаксисом означает, что он потенциально может быть неопределенным, что вы должны проверить.

Другими словами, компилятор делает именно то, что вы просите. Если вы хотите, чтобы это было необязательно, вам нужно проверить позже.


1
«компилятор делает именно то, что вы просите», поэтому моя идея ошибочна, спасибо. Мне нужно немного изменить подход.
Грасналь

В моем случае компилятор просто не осознавал, что я уже проверил объект на ноль. У меня есть получатель, который проверяет нулевое значение и вызывает этот получатель. Так что нет, он делает не совсем то, что я просил (это не значит, что я ожидаю, что он все выяснит).
CWagner

9

Это не проблема OP, но я получил то же Object is possibly 'null'сообщение, когда случайно объявил параметр как нулевой тип:

something: null;

вместо присвоения ему значения null:

something: string = null;

2
Это фактический ответ. Расстраивает, когда вы делаете явную проверку на ноль и по-прежнему получаете Object is possibly 'null'ошибку. Этот ответ решает это.
Бен

5

Как вариант, вы можете использовать приведение типов. Если у вас есть эта ошибка из машинописного текста, это означает, что некоторая переменная имеет тип или не определена:

let a: string[] | undefined;

let b: number = a.length; // [ts] Object is possibly 'undefined'
let c: number = (a as string[]).length; // ok

Будьте уверены, что aдействительно существует в вашем коде.


3

Начиная с TypeScript 3.7 ( https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html ), теперь вы можете использовать ?.оператор для получения неопределенного при доступе к атрибуту (или при вызове метода ) для нулевого или неопределенного объекта:

inputEl?.current?.focus(); // skips the call when inputEl or inputEl.current is null or undefined

Необязательный оператор доступа свойства ?..
Aventurin

2

Совет для RxJS

У меня часто будут переменные-члены типа Observable<string>, и я не буду инициализировать их до ngOnInit(используя Angular). Затем компилятор предполагает, что он неинициализирован, потому что он не «определенно назначен в конструкторе» - и компилятор никогда не пойметngOnInit .

Вы можете использовать !оператор утверждения в определении, чтобы избежать ошибки:

favoriteColor!: Observable<string>;

Неинициализированная наблюдаемая может вызвать все виды проблем во время выполнения с ошибками типа «вы должны предоставить поток, но вы указали ноль». Это !хорошо, если вы точно знаете, что оно будет установлено примерно так ngOnInit, но могут быть случаи, когда значение устанавливается другим, менее детерминированным способом.

Поэтому я иногда использую альтернативу:

public loaded$: Observable<boolean> = uninitialized('loaded');

Где uninitializedопределяется глобально где-то как:

export const uninitialized = (name: string) => throwError(name + ' not initialized');

Тогда, если вы когда-либо будете использовать этот поток без его определения, он немедленно выдаст ошибку времени выполнения.


Я не рекомендую делать это везде, но я буду делать это иногда - особенно если полагаюсь на параметры @Input, установленные извне
Simon_Weaver

2

В ReactJS я проверяю в конструкторе, являются ли переменные нулевыми, если они являются, я рассматриваю это как исключение и управляю исключением соответствующим образом. Если переменные не равны NULL, код продолжается, и компилятор больше не жалуется после этого момента:

private variable1: any;
private variable2: any;

constructor(props: IProps) {
    super(props);

    // i.e. here I am trying to access an HTML element
    // which might be null if there is a typo in the name
    this.variable1 = document.querySelector('element1');
    this.variable2 = document.querySelector('element2');

    // check if objects are null
    if(!this.variable1 || !this.variable2) {
        // Manage the 'exception', show the user a message, etc.
    } else {
        // Interpreter should not complain from this point on
        // in any part of the file
        this.variable1.disabled = true; // i.e. this line should not show the error
    }

1

Я столкнулся с этим с React при установке состояния и использования map.

В этом случае я делал вызов извлечения API, и значение ответа не было известно, но должно иметь значение «Ответ». Я использовал пользовательский тип для этого, но, поскольку значение может быть null, я все равно получил ошибку TS. Разрешение типа быть nullне может исправить это; в качестве альтернативы вы могли бы использовать default parameter value, но это было грязно для моего случая.

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

this.setState({ record: (response.Answer) ? response.Answer : [{ default: 'default' }] });

0

В машинописи вы можете сделать следующее, чтобы подавить error:

let subString?: string;

subString > !null; - Обратите внимание на добавленный восклицательный знак перед нулем.


0

Попробуйте вызвать объект так:

(<any>Object).dosomething

Эта ошибка произошла, потому что вы объявили их необязательными, используя ?. Теперь Typescript выполняет строгую проверку и не позволяет делать что-либо, что может быть undefined. Поэтому вы можете использовать (<any>yourObject)здесь.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.