Typescript: Type 'string | undefined' is not assignable to type 'string'

Typescript

Typescript Problem Overview


When I make any property of an interface optional, and while assigning its member to some other variable like this:

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 

I get an error like the following:

TS2322: Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.

How do I get around this error?

Typescript Solutions


Solution 1 - Typescript

You can now use the non-null assertion operator that is here exactly for your use case.

It tells TypeScript that even though something looks like it could be null, it can trust you that it's not:

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

Solution 2 - Typescript

To avoid the compilation error I used

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

And then validate the empty string.

Solution 3 - Typescript

I know this is a kinda late, but another way besides yannick's answer to use ! is to cast it as string thus telling TypeScript: I am sure this is a string, thus converting it.

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

to

let name1:string = person.name as string;

This will make the error go away, but if by any chance this is not a string you will get a run-time error... which is one of the reasons we are using TypeScript to ensure that the type matches and avoid such errors at compile time.

Solution 4 - Typescript

As of TypeScript 3.7 you can use nullish coalescing operator ??. You can think of this feature as a way to “fall back” to a default value when dealing with null or undefined

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

The ?? operator can replace uses of || when trying to use a default value and can be used when dealing with booleans, numbers, etc. where || cannot be used.

As of TypeScript 4 you can use ??= assignment operator as a ??= b which is an alternative to a = a ?? b;

Solution 5 - Typescript

By your definition Person.name can be null but name1 cannot. there are two scenarios:

Person.name is never null

tell the compiler your are sure the name is not null by using !

let name1: string = person.name!;

Person.name can be null

specify a default value in case name is null

let name1: string = person.name ?? "default name";

Solution 6 - Typescript

A more production-ready way to handle this is to actually ensure that name is present. Assuming this is a minimal example of a larger project that a group of people are involved with, you don't know how getPerson will change in the future.

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

let name1: string = person.name;

Alternatively, you can type name1 as string | undefined, and handle cases of undefined further down. However, it's typically better to handle unexpected errors earlier on.

You can also let TypeScript infer the type by omitting the explicit type: let name1 = person.name This will still prevent name1 from being reassigned as a number, for example.

Solution 7 - Typescript

Here's a quick way to get what is happening:

When you did the following:

name? : string

You were saying to TypeScript it was optional. Nevertheless, when you did:

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

You did not leave it a choice. You needed to have a Union on it reflecting the undefined type:

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

Using your answer, I was able to sketch out the following which is basically, an Interface, a Class and an Object. I find this approach simpler, never mind if you don't.

// 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());

Solution 8 - Typescript

try to find out what the actual value is beforehand. If person has a valid name, assign it to name1, else assign undefined.

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

Solution 9 - Typescript

Solution 1: Remove the explicit type definition

Since getPerson already returns a Person with a name, we can use the inferred type.

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

let person = getPerson();

If we were to define person: Person we would lose a piece of information. We know getPerson returns an object with a non-optional property called name, but describing it as Person would bring the optionality back.

Solution 2: Use a more precise definition

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;

Solution 3: Redesign your interface

A shape in which all properties are optional is called a weak type and usually is an indicator of bad design. If we were to make name a required property, your problem goes away.

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

Solution 10 - Typescript

if you want to have nullable property change your interface to this:

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

if being undefined is not the case you can remove question marks (?) from in front of the property names.

Solution 11 - Typescript

You can do like this!

let name1:string = `${person.name}`;

but remember name1 can be an empty string

Solution 12 - Typescript

You trying to set variable name1, witch type set as strict string (it MUST be string) with value from object field name, witch value type set as optional string (it can be string or undefined, because of question sign). If you really need this behavior, you have to change type of name1 like this:

let name1: string | undefined = person.name;

And it'll be ok;

Solution 13 - Typescript

You can use the NonNullable Utility Type:

Example

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

Docs.

Solution 14 - Typescript

If you remove the <Person> casting from your getPerson function, then TypeScript will be smart enough to detect that you return an object which definitely has a name property.

So just turn:

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;

Into:

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

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

let person = getPerson();
let name1: string = person.name;

If you cannot do that, then you will have to use the "definite assignment assertion operator" as @yannick1976 suggested:

let name1: string = person.name!;

Solution 15 - Typescript

  @Input()employee!:string ;`enter code here`
announced:boolean=false;
confirmed:boolean=false;
task:string="<topshiriq yo'q>";

  constructor(private taskService:TaskService ) {
    taskService.taskAnnon$.subscribe(
      task => {
        this.task=task;
        this.announced=true;
        this.confirmed=false;
      }
    )
  }

  ngOnInit(): void {
  }
  confirm(){
   this.confirmed=true
this.taskService.confirimTask(this.employee);`enter code here`
  }
}

Solution 16 - Typescript

This was the only solution I found to check if an attribute is undefined that does not generate warnings

type NotUndefined<T, K extends keyof T> = T & Record<K, Exclude<T[K], undefined>>;
function checkIfKeyIsDefined<T, K extends keyof T>(item: T, key: K): item is NotUndefined<T, K> {
    return typeof item === 'object' && item !== null && typeof item[key] !== 'undefined';
}

usage:


interface Listing { 
    order?: string
    ...
}
obj = {..., order: 'pizza'} as Listing
if(checkIfKeyIsDefined(item: obj, 'order')) {
    order.toUpperCase() //no undefined warning O.O
}

original answer

Solution 17 - Typescript

Had the same issue.

I find out that react-scrips add "strict": true to tsconfig.json.

After I removed it everything works great.

Edit

Need to warn that changing this property means that you:

> not being warned about potential run-time errors anymore.

as been pointed out by PaulG in comments! Thank you :)

Use "strict": false only if you fully understand what it affects!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionasdasdView Question on Stackoverflow
Solution 1 - Typescriptyannick1976View Answer on Stackoverflow
Solution 2 - TypescriptAndreFontaineView Answer on Stackoverflow
Solution 3 - TypescriptHarryView Answer on Stackoverflow
Solution 4 - TypescriptigoView Answer on Stackoverflow
Solution 5 - TypescriptAli80View Answer on Stackoverflow
Solution 6 - TypescriptgoldinsView Answer on Stackoverflow
Solution 7 - TypescriptKonkretView Answer on Stackoverflow
Solution 8 - Typescriptuser6749601View Answer on Stackoverflow
Solution 9 - TypescriptKarol MajewskiView Answer on Stackoverflow
Solution 10 - TypescriptHadi R.View Answer on Stackoverflow
Solution 11 - TypescriptTaynã Davantel CostaView Answer on Stackoverflow
Solution 12 - TypescriptDmitry LobovView Answer on Stackoverflow
Solution 13 - TypescriptИлья ЗеленькоView Answer on Stackoverflow
Solution 14 - TypescriptBenny NeugebauerView Answer on Stackoverflow
Solution 15 - Typescriptsardor khaydarovView Answer on Stackoverflow
Solution 16 - TypescriptMithsewView Answer on Stackoverflow
Solution 17 - TypescriptRosView Answer on Stackoverflow