Машинопись: Введите строку | undefined 'не назначается типу' строка '


100

Когда я делаю какое-либо свойство интерфейса необязательным, я получаю сообщение об ошибке, например, при назначении его члена какой-либо другой переменной

TS2322: Строка типа | undefined 'нельзя присвоить типу' string '. Тип undefined не может быть назначен типу string.

interface Person {
  name?:string,
  age?:string,
  gender?:string,
  occupation?:string,
}

function getPerson(){
  let person = <Person>{name:"John"};
  return person;
}
let person: Person = getPerson();
let name1:string = person.name;//<<<Error here 

Как мне обойти эту ошибку?

Ответы:


182

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

Он сообщает TypeScript, что даже если что-то выглядит так, как будто оно может быть нулевым, он может вам доверять, что это не так:

let name1:string = person.name!; 
//                            ^ note the exclamation mark here  

3
Это именно то, что я искал!
— Shautieh

1
Пока ESLint не ограничивает использование ненулевого оператора утверждения :)
— Анатолий

3
Я в шоке, что такая вещь должна существовать. 🤯
— GONeale

@GONeale есть много случаев, когда транспилятор не может самостоятельно определить, что значение не может быть нулевым. Одним из примеров является значение, установленное, например, Angular, и Angular имеет свой собственный способ гарантировать, что значение не может быть нулевым.
— yannick1976

18
Это ужасный совет! Вместо этого помогите компилятору разобраться в этом. Добавьте проверку перед доступом к свойству, где вы либо бросаете, либо возвращаете, если имя не установлено.
— geon

56

Чтобы избежать ошибки компиляции, я использовал

let name1:string = person.name || '';

А затем проверьте пустую строку.


1
Что, если это не строка, а объект?
— Анатолий

1
Если это объект, то вместо "" это будет {}
— Крити

Это то, что вы можете сделать, когда вы связываете значения и получаете такую ​​ошибкуOptional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong.
— ТЕСИД

26

Я знаю, что это какой-то поздний ответ, но другой способ, помимо ответа Янника https://stackoverflow.com/a/57062363/6603342, чтобы использовать! состоит в том, чтобы преобразовать его как строку, тем самым сообщая TypeScript, что я уверен, что это строка, таким образом преобразуя

let name1:string = person.name;//<<<Error here 

к

let name1:string = person.name as string;

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


Это может устранить ошибку, но это никогда не изменит фактический тип переменной.
— манси

Извините, но я думаю, что вы совершенно ошибаетесь, поскольку я уже упоминал, что это фактически изменит тип, поскольку он вызовет исключение времени выполнения, если тип не является строкой или не имеет метода toString. Это приведет к преобразованию всего, что есть, в строку, т.е. у вас есть номер, который будет пытаться вызвать функцию .toString того, что у вас есть.
— Гарри

Но это будет неправильный путь, скажем, если в моем имени будет номер «0», он просто превратит его в строку ... Скорее он должен выдать ошибку и сказать недопустимое имя. Лично я бы не стал использовать это приведение типов и специально проверять тип, переданный в объекте .. но у всех нас разные стили кодирования, открытые для дальнейшего обсуждения одного и того же
— манси,

Да, но это полностью вне контекста вопроса, на который был дан ответ. Мы не знаем, является ли это формой, если нет никаких шагов, чтобы отфильтровать ее заранее. Также то, что вы предлагаете, было бы невозможно на этом этапе без некоторых действительно грязных обходных путей.
— Гарри

Нет необходимости в обходных путях, достаточно одной проверки, прежде чем пытаться получить доступ к переменной. Оба случая будут обработаны легко, ошибки исчезнут, и ваш код станет более безопасным. Просто попробуйте :)
— манси

21

Начиная с TypeScript 3.7 вы можете использовать нулевой оператор объединения ?? . Вы можете думать об этой функции как о способе «возврата» к значению по умолчанию при работе с null или undefined.

let name1:string = person.name ?? '';

??Оператор может заменить использование ||при попытке использовать значение по умолчанию , и может быть использована при работе с Булевыми, номером и т.д. , где ||не может быть использован.

Начиная с TypeScript 4 вы можете использовать ??=оператор присваивания, a ??= bкоторый является альтернативойa = a ?? b;


7

постарайтесь узнать, какова действительная стоимость заранее. Если personесть действительный name, назначьте его name1, иначе назначьте undefined.

let name1: string = (person.name) ? person.name : undefined;

2
хммм. Это будет слишком многословно, если я захочу выполнить несколько заданий. Есть ли способ отключить это конкретное предупреждение?
— asdasd

к сожалению, кроме того, что вы сделаете это nameобязательным, убрав вопросительный знак.
— Lynx 242

PS: Какую версию TypeScript вы используете? Я использую 3.2.4 и не сталкиваюсь с этим сообщением. В моем тестовом классе код компилируется правильно.
— Lynx 242

та же версия. Я получаю эту ошибку в webstorm, но не в vscode
— asdasd

Это странно, потому что я использую IntelliJ, который должен быть похож на WebStorm, когда дело касается применения TS-правил ?!
— Lynx 242

5

Более готовый к производству способ справиться с этим - убедиться, что nameон присутствует. Предполагая, что это минимальный пример более крупного проекта, в котором участвует группа людей, вы не знаете, как getPersonэто изменится в будущем.

if (!person.name) {
    throw new Error("Unexpected error: Missing name");
}

let name1: string = person.name;

Кроме того, вы можете ввести name1как string | undefinedи обрабатывать случаи, когда они undefinedнаходятся ниже. Однако обычно лучше обрабатывать неожиданные ошибки раньше.

Вы также можете позволить TypeScript определять тип, опуская явный тип: let name1 = person.nameэто все равно предотвратит name1переназначение, например, как число.


3

Вот быстрый способ узнать, что происходит:

Когда вы сделали следующее:

название? : строка

Вы говорили TypeScript, что это необязательно. Тем не менее, когда вы это сделали:

let name1 : string = person.name; //<<<Error here 

Вы не оставили ему выбора. На нем должен быть Union, отражающий неопределенный тип:

let name1 : string | undefined = person.name; //<<<No error here 

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

// Interface
interface iPerson {
    fname? : string,
    age? : number,
    gender? : string,
    occupation? : string,
    get_person?: any
}

// Class Object
class Person implements iPerson {
    fname? : string;
    age? : number;
    gender? : string;
    occupation? : string;
    get_person?: any = function () {
        return this.fname;
    }
}

// Object literal
const person1 : Person = {
    fname : 'Steve',
    age : 8,
    gender : 'Male',
    occupation : 'IT'  
}

const p_name: string | undefined = person1.fname;

// Object instance 
const person2: Person = new Person();
person2.fname = 'Steve';
person2.age = 8;
person2.gender = 'Male';
person2.occupation = 'IT';

// Accessing the object literal (person1) and instance (person2)
console.log('person1 : ', p_name);
console.log('person2 : ', person2.get_person());

2

Вы пытаетесь установить переменную name1, тип ведьмы установлен как строгая строка (это ДОЛЖНА быть строка) со значением из поля объекта name, тип значения ведьмы установлен как необязательная строка (это может быть строка или неопределенное значение из-за знака вопроса). Если вам действительно нужно такое поведение, вы должны изменить его тип name1следующим образом:

let name1: string | undefined = person.name;

И все будет хорошо;


0

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

const { DB_HOST = "localhost", DB_PORT: "5432" } = process.env

db.connect(DB_HOST, DB_PORT);
const connectToDb = (host = "localhost", port: "5432") => db.connect(host, port);

0

Вы можете использовать необязательную цепочку Typescript. Пример:

const name = person?.name;

Если свойство nameсуществует для personобъекта, вы получите его значение, но в противном случае оно автоматически вернет undefined.

Вы можете использовать этот ресурс для лучшего понимания.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html



-1

Решение 1. Удалите явное определение типа

Поскольку getPersonуже возвращает a Personс именем, мы можем использовать выведенный тип.

function getPerson(){
  let person = {name:"John"};
  return person;
}

let person = getPerson();

Если бы мы дали определение, person: Personмы бы потеряли часть информации. Мы знаем, что getPersonвозвращает объект с вызванным необязательным свойством name, но описание его как Personбы вернет опциональность.

Решение 2. Используйте более точное определение

type Require<T, K extends keyof T> = T & {
  [P in K]-?: T[P]
};

function getPerson() {
  let person = {name:"John"};
  return person;
}

let person: Require<Person, 'name'> = getPerson();
let name1:string = person.name;

Решение 3. Измените дизайн интерфейса

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

interface Person {
  name:string,
  age?:string,
  gender?:string,
  occupation?:string,
}

-14

Была такая же проблема.

Я обнаружил, что сценарии реакции добавляют "strict": trueк tsconfig.json.

После удаления все отлично работает.

редактировать

Необходимо предупредить, что изменение этого свойства означает, что вы:

больше не получать предупреждений о потенциальных ошибках времени выполнения.

как было указано PaulG в комментариях! Спасибо :)

Используйте "strict": falseтолько в том случае, если полностью понимаете, на что это влияет!


9
Это может «сработать». Но это работает только в том смысле, что вас больше не предупреждают о потенциальных ошибках времени выполнения. Я бы поощрять исследования о том , что делает строгая проверка в машинописном: medium.com/webhint/going-strict-with-typescript-be3f3f7e3295
— PaulG

5
Я должен упомянуть, что OP спросил: «Как мне обойти эту ошибку?» а не "Как мне исправить эту проблему?"
— devinbost

Простое добавление name: string = undefined (как рекомендовано такими пакетами, как json2typescript) вызовет ошибку 2322, если вы не отключите строгое значение, что потребуется большинству людей. Этот принятый ответ ясен, верен и заслуживает еще большего количества голосов.
— AUSTX_RJL 05
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.