Maszynopis: Wpisz „ciąg | undefined „nie można przypisać do typu„ string ”


114

Kiedy ustawiam dowolną właściwość interfejsu jako opcjonalną, popełniam błąd jak naśladowanie podczas przypisywania jej elementu członkowskiego do innej zmiennej

TS2322: Wpisz „ciąg znaków | undefined ”nie można przypisać do typu„ string ”. Typu „undefined” nie można przypisać do typu „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 

Jak obejść ten błąd?

Odpowiedzi:


228

Możesz teraz użyć niezerowego operatora potwierdzenia, który jest tutaj dokładnie dla twojego przypadku użycia.

Mówi TypeScript, że nawet jeśli coś wygląda na to, że może być zerowe, może ci ufać, że tak nie jest:

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

4
Właśnie tego szukałem!
— Shautieh

1
Dopóki ESLint nie ograniczy używania operatora asercji innego niż null :)
— Anatoly

4
Jestem w szoku, że coś takiego musi istnieć. 🤯
— GONeale

23
To straszna rada! Zamiast tego pomóż kompilatorowi to rozgryźć. Dodaj kontrolę przed uzyskaniem dostępu do właściwości, w której możesz rzucać lub zwracać, jeśli nazwa nie jest ustawiona.
— geon

2
Nie zgadzam się z tymi dwoma ostatnimi komentarzami ... Np. Renderuję warunkowo element wizualny, jeśli właściwość istnieje, a do tego elementu jest dołączona funkcja, która korzysta z tej właściwości. Dodatkowe sprawdzenie wartości niezerowej dla właściwości wewnątrz funkcji jest niepotrzebne
— eazy_g

62

Aby uniknąć błędu kompilacji, którego użyłem

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

A następnie zweryfikuj pusty ciąg.


1
A co, jeśli nie jest to ciąg, ale obiekt?
— Anatolij

2
Jeśli to obiekt, to zamiast „” będzie {}
— Kriti

To jest coś, co możesz zrobić, gdy łączysz wartości i otrzymujesz tego rodzaju błądOptional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong.
— THeSiD

31

Wiem, że to trochę późna odpowiedź, ale inny sposób niż odpowiedź Yannick https://stackoverflow.com/a/57062363/6603342 do użycia! polega na rzutowaniu go jako ciągu, w ten sposób mówiąc TypeScript, że jestem pewien, że jest to ciąg, a zatem konwertuje

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

do

let name1:string = person.name as string;

Spowoduje to, że błąd zniknie, ale jeśli przypadkiem nie jest to ciąg , otrzymasz błąd czasu wykonania, który jest jednym z powodów, dla których używamy TypeScript, aby upewnić się, że typ jest zgodny i uniknąć takich błędów w czasie kompilacji .


1
Może to spowodować, że błąd zniknie, ale nigdy nie zmieni rzeczywistego typu zmiennej.
— Mansi

Przykro mi, ale myślę, że się mylisz, ponieważ wspomniałem, że w rzeczywistości zmieni to typ, ponieważ zgłosi wyjątek w czasie wykonywania, jeśli typ nie jest ciągiem lub nie ma metody toString. To przerzuci wszystko, co tam jest, na łańcuch, tj. Masz liczbę, którą spróbuje wywołać funkcję .toString tego, co tam masz.
— Harry,

2
Ale to nie będzie właściwa droga, powiedzmy, że jeśli moje imię ma liczbę „0”, po prostu zamieniłoby to na łańcuch… Powinien raczej zgłosić błąd i powiedzieć nieprawidłową nazwę. Osobiście nie użyłbym tego typu, a szczególnie sprawdziłbym typ przekazany w obiekcie .. ale wszyscy mamy różne style kodowania, otwarte na więcej dyskusji na ten sam temat
— Mansi.

Tak, ale to zupełnie poza kontekstem pytania, na które udzielono tej odpowiedzi. Nie wiemy, czy jest to formularz, jeśli nie ma żadnych kroków, aby go wcześniej odfiltrować. Również to, co sugerujesz, nie byłoby możliwe na tym etapie bez naprawdę brudnych obejść.
— Harry,

1
Nie ma potrzeby stosowania obejść, wystarczy jedno sprawdzenie, zanim spróbujesz uzyskać dostęp do zmiennej. Oba przypadki byłyby łatwe do obsłużenia, błędy zniknęłyby, a kod byłby bezpieczniejszy. Po prostu wypróbuj :)
— Mansi

28

Począwszy od TypeScript 3.7 można używać zerowego operatora łączenia ?? . Możesz myśleć o tej funkcji jako o sposobie „powrotu” do wartości domyślnej, gdy mamy do czynienia z wartością null lub undefined

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

??Operator może zastąpić zastosowania ||podczas próby użycia wartości domyślnej i może być używany w kontaktach z logicznych, liczb itp gdzie ||nie mogą być użyte.

Począwszy od TypeScript 4 można używać ??=operatora przypisania, a ??= bktóry jest alternatywą dlaa = a ?? b;


8

Bardziej gotowym do produkcji sposobem radzenia sobie z tym jest faktyczna nameobecność. Zakładając, że jest to minimalny przykład większego projektu, w który zaangażowana jest grupa ludzi, nie wiesz, jak getPersonto się zmieni w przyszłości.

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

let name1: string = person.name;

Alternatywnie możesz pisać name1jako string | undefinedi obsługiwać przypadki w undefineddalszej części. Jednak zazwyczaj lepiej jest zająć się nieoczekiwanymi błędami wcześniej.

Możesz również pozwolić TypeScript wywnioskować typ, pomijając typ jawny: na przykład let name1 = person.namezapobiegnie to ponownemu name1przypisaniu jako liczby.


7

spróbuj wcześniej dowiedzieć się, jaka jest rzeczywista wartość. Jeśli personma ważny name, przypisz go do name1, w przeciwnym razie przypisz undefined.

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

2
hmmm. To będzie zbyt szczegółowe, jeśli chcę wykonać wiele zadań. Czy istnieje sposób na wyłączenie tego konkretnego ostrzeżenia?
— asdasd

niestety nie, chyba że wprowadzisz nameobowiązkowe, usuwając znak zapytania.
— Lynx 242

PS: Której wersji TypeScript używasz? Używam 3.2.4 i nie napotykam tej wiadomości. W mojej klasie testowej kod dokładnie się kompiluje.
— Lynx 242

ta sama wersja. Ten błąd pojawia się w burzy internetowej, ale nie w vscode
— asdasd

To dziwne, ponieważ używam IntelliJ, który ma być podobny do WebStorm, jeśli chodzi o stosowanie reguł TS ?!
— Lynx 242

4

Oto szybki sposób sprawdzenia tego, co się dzieje:

Kiedy wykonałeś następujące czynności:

Nazwa? : strunowy

Mówiłeś do TypeScript, że to opcjonalne. Niemniej jednak, kiedy to zrobiłeś:

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

Nie zostawiłeś tego wyboru. Musiałeś mieć na nim Unię odzwierciedlającą niezdefiniowany typ:

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

Korzystając z twojej odpowiedzi, mogłem naszkicować następujący, który jest w zasadzie interfejsem, klasą i obiektem. Uważam, że to podejście jest prostsze, nieważne, jeśli nie.

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

Próbujesz ustawić zmienną name1, typ wiedźmy ustawiony jako ścisły ciąg (MUSI to być łańcuch) z wartością z pola obiektu name, typ wartości ustawiony jako opcjonalny ciąg (może to być ciąg lub nieokreślony, ze względu na znak zapytania). Jeśli naprawdę potrzebujesz tego zachowania, musisz zmienić jego typ name1:

let name1: string | undefined = person.name;

I będzie dobrze;


0

Możesz użyć NonNullabletypu narzędzia:

Przykład

type T0 = NonNullable<string | number | undefined>;  // string | number
type T1 = NonNullable<string[] | null | undefined>;  // string[]

Dokumenty .



-1

Rozwiązanie 1: Usuń jawną definicję typu

Ponieważ getPersonjuż zwraca a Personz nazwą, możemy użyć wywnioskowanego typu.

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

let person = getPerson();

Gdybyśmy mieli zdefiniować person: Person, stracilibyśmy część informacji. Wiemy, że getPersonzwraca obiekt z wywołaną nie opcjonalną właściwością name, ale opisanie go w Persontaki sposób, aby przywrócić opcjonalność.

Rozwiązanie 2: Użyj dokładniejszej definicji

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;

Rozwiązanie 3: Przeprojektuj interfejs

Kształt, w którym wszystkie właściwości są opcjonalne, nazywany jest typem słabym i zwykle jest wskaźnikiem złego projektu. Gdybyśmy mieli wykonać namewymaganą nieruchomość, Twój problem zniknie.

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

-14

Miałem ten sam problem.

I okazuje się, że reagują-scrips dodać "strict": truedo tsconfig.json.

Po usunięciu wszystko działa świetnie.

Edytować

Musisz ostrzec, że zmiana tej właściwości oznacza, że:

nie jest już ostrzegany o potencjalnych błędach w czasie wykonywania.

jak zauważył PaulG w komentarzach! Dziękuję Ci :)

Używaj "strict": falsetylko wtedy, gdy w pełni rozumiesz, na co wpływa!


9
To może „zadziałać”. Ale to działa tylko w tym sensie, że nie jesteś już ostrzegany o potencjalnych błędach czasu wykonywania. Zachęcam do zbadania, co robi ścisłe sprawdzanie w
— Typescript

5
Powinienem wspomnieć, że OP zadał pytanie „Jak obejść ten błąd?” nie, „Jak rozwiązać ten problem?”
— devinbost

Samo dodanie name: string = undefined (zgodnie z zaleceniami takich pakietów jak json2typescript) spowoduje wyświetlenie błędu 2322, chyba że wyłączysz ścisłe, co większość ludzi będzie musiała zrobić. Ta zaakceptowana odpowiedź jest jasna, poprawna i zasługuje na znacznie więcej głosów.
— AUSTX_RJL,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.