You can use the type utility Partial<Type> when initializing the value to indicate that the properties have not yet been defined:
let user: Partial<Person> = {};
After initializing the property values, TypeScript will correctly infer that they do indeed exist:
user.age = 22;
user.name = "Dave";
user.city = "NY";
const doubleAge = user.age * 2; // Ok
const lowercaseCity = user.city.toLowerCase(); // Ok
const uppercaseName = user.name.toUpperCase(); // Ok
TS Playground
However, if you do not initialize one of the property values, TypeScript will emit an error diagnostic when trying to use it:
let user: Partial<Person> = {};
user.age = 22;
const doubleAge = user.age * 2; // Ok
const lowercaseCity = user.city.toLowerCase(); /* Error
~~~~~~~~~
'user.city' is possibly 'undefined'.(18048) */
const uppercaseName = user.name.toUpperCase(); /* Error
~~~~~~~~~
'user.name' is possibly 'undefined'.(18048) */
TS Playground
There are still some scenarios where the inference isn't enough — for example, when the value needs to be used as an argument to a function which requires a Person:
declare function doSomethingWithPerson(person: Person): void;
let user: Partial<Person> = {};
user.age = 22;
user.name = "Dave";
user.city = "NY";
doSomethingWithPerson(user); /* Error
~~~~
Argument of type 'Partial<Person>' is not assignable to parameter of type 'Person'.
Property 'name' is optional in type 'Partial<Person>' but required in type 'Person'.(2345) */
TS Playground
In these cases, you can create a function called a type guard which will check at runtime to ensure that the type meets the stated expectations. A user-defined type guard is a function whose return type is a type predicate:
function isPerson<T extends Record<PropertyKey, unknown>>(
p: T,
): p is T & Person {
return (
typeof p.age === "number" &&
typeof p.city === "string" &&
typeof p.name === "string"
);
}
See also in the handbook: generics
Then you can check whether user actually meets the definition of Person before using it in order to avoid errors:
let user: Partial<Person> = {};
user.age = 22;
user.name = "Dave";
user.city = "NY";
if (isPerson(user)) {
doSomethingWithPerson(user); // Ok
} else {
// Handle the other case here
}
TS Playground