Optional Properties are those properties that are not mandatory. In the example below the person
object name
is usually mandatory but age
may not be. When we create a person object without assigning a value to age
the compiler throws age
is missing in a type error. To solve this problem, we need to mark the property as optional.
1 2 3 4 5 6 7 8 9 10 11 | type Person= { name: string; age:number; } let person:Person= { name:"Eric J. Cerda"} //Property 'age' is missing in type '{ name: string; }' but required in type 'Person'. |
Table of Contents
Creating Optional Properties in TypeScript
To make a specific property optional just add a question mark (?) after the property name
1 2 3 | let person : {name:string, age?:number} = { name:"Eric J. Cerda"} |
1 2 3 4 5 6 7 8 9 10 11 12 13 | type Person= { name: string; age?:number; } let person:Person = { name:"Eric J. Cerda", } console.log(person.age) //undefined |
Interface
1 2 3 4 5 6 7 8 9 10 11 12 13 | interface Person { name: string; age?:number; } let person:Person = { name:"Eric J. Cerda", } console.log(person.age) //undefined |
Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Person { name: string; age?:number constructor(name:string) { this.name=name } } let person= new Person("Eric J. Cerda") console.log(person.age) //undefined |
Note that we did not include age
in the class constructor function. If you want to include that then make sure you mark it as an optional parameter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Person { name: string; age?:number //optional constructor(name:string, age?:number) { //age is optional here this.name=name this.age=age } } let person= new Person("Eric J. Cerda") console.log(person.age) //undefined |
Optional Property and Undefined
Adding ?
makes age
as optional property. Trying to access the value of age
returns undefined
as expected.
1 2 3 4 5 6 7 8 9 10 | interface Person { name: string, age?: number; } let person:Person = {name:"Eric J. Cerda"} //ok console.log(person.age) //undefined |
But typescript also allows us to assign undefined
value to an optional property although age
is of type number
.
1 2 3 4 5 6 7 8 | interface Person { name: string, age?: number; } let person:Person = {name:"Eric J. Cerda", age:undefined} //ok |
This is because TypeScript treats every optional property as a union type of undefined
and assigned type.
1 2 3 4 5 6 7 8 9 10 11 12 13 | interface Person { name: string, age?: number; } //The above interface is treated as interface Person { name: string, age?: number|undefined; } |
ExactOptionalPropertyTypes Config Option
In the previous section, we showed you that typescript allows us to assign undefined to an optional property.
1 2 3 4 5 6 7 8 | interface Person { name: string, age?: number; } let person:Person = {name:"Eric J. Cerda", age:undefined} //ok |
You can stop that by setting the exactOptionalPropertyTypes
to true in the tsconfig.json
file under the section compilerOptions
. Note that you also need to enable strictNullChecks
for the exactOptionalPropertyTypes
to work.
1 2 3 4 5 6 7 8 9 10 | { "compilerOptions": { "exactOptionalPropertyTypes": true, "strictNullChecks" : true }, } |
With exactOptionalPropertyTypes enabled, the TypeScript compiler throws an error when we try to assign the undefined to the age variable.
1 2 3 4 5 6 7 8 9 10 11 12 | interface Person { name: string, age?: number; } let person:Person = {name:"Eric J. Cerda", age:undefined} //error //Type '{ name: string; age: undefined; }' is not assignable to type 'Person' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. //Types of property 'age' are incompatible. |
You can override the behavior by explicitly setting the type as undefined
1 2 3 4 5 6 7 8 | interface Person { name: string, age?: number|undefined; } let person:Person = {name:"Eric J. Cerda", age:undefined} |
Make all properties optional of an existing type
Typescript utility types allow us to construct a new type from an existing type. The Partial utility type is one such utility that takes a type and constructs a new type with all the properties set to optional.
The following is the syntax for converting an existing type to optional.
1 2 3 | Partial<exitingType> |
In this example, the Person interface has all properties marked as required. Assigning it an empty object will result in an error.
1 2 3 4 5 6 7 8 9 10 | interface Person { name:string; address:string age:number } let person:Person= {} //Type '{}' is missing the following properties from type 'Person': name, age |
You can use the Partial<Person>
to create a new type with all properties set to optional. Hence it will not result in any compiler error.
1 2 3 4 5 6 7 8 9 | interface Person { name:string; address:string age:number } let person:Partial<Person>= {} //No error. All Properties are now optional in person object |
The following is one of the use cases for the Partial
utility type.
The updatePerson
function updates the subset of the properties of person
object. The second argument to fields: Partial<Person>
create fields
object with a type of Person
interface with all properties set to optional. This allows us to pass any combination arguments to updatePerson
. It also provides a strong type of checking as we cannot pass a non-existing property.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | interface Person { name: string; address: string; age: number; } function updatePerson(person: Person, fields: Partial<Person>):Person { person = { ...person, ...fields } return person; } let person: Person = { name: "Eric J. Cerda", address: "Washington", age: 50, }; //We can pass anything to fields argument as long as property exists in person interface //update age & address person = updatePerson(person, { age: 60, address:"San Francisco" }); //ok //update address only person = updatePerson(person, { address:"Washington" }); //ok //update non existing property city person = updatePerson(person, { city:"Washington" }); //error //Argument of type '{ city: string; }' is not assignable to parameter of //type 'Partial<Person>'. |