Examples
Making optional code more strict
Before
interface Address {
street: string;
city?: string;
}
interface User {
name: string;
address: Address;
meta: Record<string, string>;
}
interface SuperUser extends User {
permissions: string[];
}
class UserRepository {
private users: User[];
constructor() {
this.users = [
// Do not change the data. Let's assume it comes from the backend.
{
name: "John",
address: undefined,
meta: { created: "2019/01/03" }
},
{
name: "Anne",
address: { street: "Warsaw" },
meta: {
created: "2019/01/05",
modified: "2019/04/02"
}
}
];
}
getUser(id: number): User | undefined {
return this.users[id];
}
getCities() {
return this.users
.filter(user => user.address?.city)
.map(user => user.address.city);
}
forEachUser(action: (user: User) => void) {
this.users.forEach(user => action(user));
}
}
const userRepository = new UserRepository();
console.log(userRepository.getUser(1).address.city.toLowerCase());
console.log(
userRepository
.getCities()
.map(city => city.toUpperCase())
.join(", ")
);
console.log(new Date(userRepository.getUser(0).meta.modfified).getFullYear());
After
interface Address {
street: string;
city?: string;
}
interface User {
name: string;
address: Address | undefined; /* 1 */
meta: Record<string, string>;
}
interface SuperUser extends User {
permissions: string[];
}
class SafeUserRepository {
private users: User[];
/* 2 */
constructor() {
this.users = [
// Do not change the data. Let's assume it comes from the backend.
{
name: "John",
address: undefined,
meta: { created: "2019/01/03" }
},
{
name: "Anne",
address: { street: "Warsaw" },
meta: {
created: "2019/01/05",
modified: "2019/04/02"
}
}
];
}
// `user` with given `id` might not exist, so marking the return type as possibly undefined
getUser(id: number /* 3 */): User | undefined /* 4 */ {
return this.users[id];
}
getCities() {
return this.users
/* 5 */
.map(user => user.address?.city)
.filter(city => city !== undefined);
}
forEachUser(action: (user: User) => void) {
this.users.forEach(user => action(user));
}
}
const safeUserRepository = new SafeUserRepository();
/* 6 */
console.log(safeUserRepository.getUser(1)?.address?.city?.toLowerCase());
console.log(
safeUserRepository
.getCities()
/* 7 */
.map(city => city?.toUpperCase())
.join(", ")
);
/* 8 */
console.log(new Date(safeUserRepository?.getUser(0)?.meta.modfified ?? 0).getFullYear());