Як придушити "помилку TS2533: Об'єкт, можливо," нульовий "або" невизначений "?


225

У мене є 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, як я бачу відповіді тут. Дякую за думки.
— граснал

25
Навіть ініціалізація не пригнічує "Об'єкт, можливо, 'не визначений" в TS 2.7.2
— Tyguy7,

1
Так, але ці визначення змінюють значення об'єктів, наприклад, HTMLDivElement не має найближчих цільових та інших базових елементів подій та властивостей.
— Кларенс

5
Що робити, якщо ви намагаєтесь описати стан якоїсь властивості Javascript Object, де справжнє представлення початкового стану null?
— Тимур Мамедов

1
Якщо додати, що в !операторі є оператор, дуже схожий на той, що в Котліні, що дозволяє зробити ці перевірки набагато більш короткими
— Альваро

600

Якщо ви знаєте із зовнішніх засобів, що вираз не є, 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/isissue/11494 ), тому кожен, хто шукає відповіді, читає це http://stackoverflow.com/questions/38874928/operator-in-typescript -after-object-method
— grasnal

9
це також не впливає на tsc v2.7.2
— Tyguy7,

6
@ThomasSauvajon !оператор тут не робить те саме, що ?в C # does! Це лише типове твердження системи ; це не спричинить збій вашої програми при спробі читання ресурсу з nullабо undefined.
— Райан Кавано

2
З підручника, доданого при викладенні Райана: Синтаксис є постфіксом !: identifier!видаляє nullі undefinedз типуidentifier Це те, про що вже говорив Райан, але я вважаю цей спосіб також корисним.
— Джон Хаттон

5
Це не працює в Typescript 3.2.2. Вилучено?
— Lars Nyström

25

Я використав:

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

Крім того, ви можете використовувати тип примусу:

const objectX = object as string

Хоча, перш ніж вибрати одне з перерахованих вище способів, будь ласка, врахуйте архітектуру, до якої ви прагнете, і це вплине на загальну картину.


57
чомусь мій ТСК ігнорує, що якщо заява, все-таки вважає, що це може бути невизначено ...
— 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 та додайте "строгийNullChecks": false

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


Це працювало і для мене. Незважаючи на те, що вона все ще дає помилку, як, наприклад, в операторах підписки, вона не розпізнає змінну результату, typecript хоче, щоб вона оголосила .subscribe (result => this.result = result.json ());
— Aarchie

Ви спробували скористатися оператором "map"? Google "rxjs / map". Я в основному роблю:. Http.get (...). Map (result => result.json ()). Підписатися (результат => {зробіть тут свої речі})
— Mahesh Nepal

38
не відповідає на запитання. ОП прямо сказала: "без відключення перевірок компілятора TS"
— pvill

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

12

Якщо ви знаєте, що тип ніколи не буде nullабо undefined, вам слід оголосити його як foo: Barбез ?. Оголошення типу за допомогою ? Barсинтаксису означає, що він потенційно може бути невизначений, що потрібно перевірити.

Іншими словами, компілятор робить саме те, про що ви просите. Якщо ви хочете, щоб це було необов’язково, вам потрібно буде перевірити пізніше.


1
"компілятор робить саме те, про що ви просите", тому моя ідея неправильна, дякую. Мені потрібно трохи змінити підхід.
— grasnal

У моєму випадку компілятор просто не зрозумів, що я вже перевірив об'єкт на нуль. У мене є геттер, який перевіряє наявність нуля і викликає цього. Так ні, це не робить саме те, про що я його просив (це не означає, що я очікую, що він все зрозуміє).
— CWagner

9

Це не проблема ОП, але я отримав те саме Object is possibly 'null'повідомлення, коли випадково оголосив параметр, як нульовий тип:

something: null;

замість того, щоб присвоїти йому значення null:

something: string = null;

2
Це фактична відповідь. Розчарування, коли ви робите фактичну явну нульову перевірку, і все одно отримуєте Object is possibly 'null'помилку. Ця відповідь вирішує це.
— Бен Рачикот

5

Як варіант, ви можете використовувати типовий кастинг. Якщо у вас є помилка у typecript, це означає, що деяка змінна має тип або не визначена:

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 я перевіряю в конструкторі, чи змінні є нульовими, якщо вони є, я трактую це як виняток і керую виняток належним чином. Якщо змінні не є нульовими, код продовжує діяти, і компілятор після цього моменту більше не скаржиться:

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 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.