Reputation: 7200
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?
Upvotes: 620
Views: 1235353
Reputation: 189
Please try the below example. This worked for me.
interface Listing {
name: string | undefined ,
}
export class Listing {
public listings: Listing[] = []
constructor(){}
ngOnInit(){
this.listings = fakeListing;
}
}
update the html file as below example with &&
<p>First Name: {{listing.name && listing.name.split(" ")[0] }}</p>
Upvotes: 0
Reputation: 41
Adding condition using ternary operator. The purpose of this line is to assign name1
the value of person.name
if it is defined, and an empty string if not.
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 ? person.name : ''; // Added condition
Upvotes: 2
Reputation: 3917
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 compile time
to
let name1 = person.name as string; // No error here compile time, runtime though
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.
Upvotes: 293
Reputation: 8121
You could tighten up your types. Your getPerson
function says it returns a Person
, which implies that every property in the resulting value may be optional/undefined. But given the context of getPerson
, we can make a stronger statement: the resulting value certainly has a name
property! I would refactor getPerson
to be:
function getPerson() {
return { name: 'John' } as <Person & { name: string }>;
}
Now getPerson().name
will be typed as string
, not undefined | string
.
You could go even further, and remove the typing altogether:
function getPerson() { return { name: 'John' }; }
Typescript will infer the type from the returned value, and again, getPerson().name
registers as type string
.
Upvotes: 1
Reputation: 2288
One thing that was happening to me in my particular situation (but it is not related with the specific problem asked here) was that I was importing the wrong type in my file, given that the type was called exactly the same, but defining different properties.
Once I imported the correct type, all the issues dissapeared.
I hope this can help someone that's facing the same as me.
Upvotes: 0
Reputation: 41
I think to use Require
as mentioned by Karol Majewski is quite nice. Another way to achieve the same would be to use intersection types(which is actually used internally by Require
)
function getPerson(): Person & {name: string} {
const person = {name:"John"};
return person;
}
const person = getPerson();
const name1: string = person.name;
The advantage of using Require
or intersection types is that we don't overrule the typescript compiler as it happens for the non-null assertion operator.
Upvotes: 1
Reputation: 1766
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
Upvotes: 3
Reputation: 109
You can do like this!
let name1:string = `${person.name}`;
but remember name1
can be an empty string
Upvotes: 5
Reputation: 8626
By your definition Person.name
can be null but name1
cannot.
there are two scenarios:
Person.name
is never nulltell the compiler your are sure the name is not null by using !
let name1: string = person.name!;
Person.name
can be nullspecify a default value in case name is null
let name1: string = person.name ?? "default name";
Upvotes: 58
Reputation: 11565
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
Upvotes: 981
Reputation: 54772
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!;
Upvotes: 2
Reputation: 403
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.
Upvotes: 4
Reputation: 25770
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,
}
Upvotes: 6
Reputation: 6828
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;
Upvotes: 82
Reputation: 1376
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.
Upvotes: 30
Reputation: 8078
You can use the NonNullable
Utility Type:
Example
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]
Docs.
Upvotes: 3
Reputation: 1021
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());
Upvotes: 15
Reputation: 303
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;
Upvotes: 4
Reputation: 501
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!
Upvotes: -14
Reputation: 2584
To avoid the compilation error I used
let name1:string = person.name || '';
And then validate the empty string.
Upvotes: 210
Reputation:
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;
Upvotes: 13