TypeScript Tips and Tricks for Writing Better Code
Are you tired of writing code that is hard to read and maintain? Do you want to improve your TypeScript skills and write better code? Look no further! In this article, we will share some tips and tricks for writing better TypeScript code.
Use Strict Mode
TypeScript has a strict mode that helps catch common errors at compile-time. Enabling strict mode can help you write more robust code and catch errors before they become runtime errors. To enable strict mode, add the following line to your tsconfig.json
file:
{
"compilerOptions": {
"strict": true
}
}
Use Interfaces
Interfaces are a powerful feature of TypeScript that allow you to define the shape of an object. By using interfaces, you can ensure that your code is type-safe and avoid runtime errors. Here's an example:
interface Person {
name: string;
age: number;
}
function greet(person: Person) {
console.log(`Hello, ${person.name}! You are ${person.age} years old.`);
}
const john: Person = { name: 'John', age: 30 };
greet(john);
In this example, we define an interface Person
that has two properties: name
and age
. We then define a function greet
that takes a Person
object as an argument and logs a greeting to the console. Finally, we create a Person
object john
and pass it to the greet
function.
Use Enums
Enums are another powerful feature of TypeScript that allow you to define a set of named constants. By using enums, you can make your code more readable and avoid magic numbers. Here's an example:
enum Color {
Red,
Green,
Blue,
}
function getColorName(color: Color) {
switch (color) {
case Color.Red:
return 'red';
case Color.Green:
return 'green';
case Color.Blue:
return 'blue';
default:
throw new Error(`Unknown color: ${color}`);
}
}
const color = Color.Red;
console.log(getColorName(color)); // Output: "red"
In this example, we define an enum Color
that has three constants: Red
, Green
, and Blue
. We then define a function getColorName
that takes a Color
enum as an argument and returns the corresponding color name. Finally, we create a Color
variable color
and pass it to the getColorName
function.
Use Type Aliases
Type aliases are a way to create a new name for an existing type. By using type aliases, you can make your code more readable and avoid repeating complex types. Here's an example:
type Point = {
x: number;
y: number;
};
function distance(a: Point, b: Point) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
const a: Point = { x: 0, y: 0 };
const b: Point = { x: 3, y: 4 };
console.log(distance(a, b)); // Output: 5
In this example, we define a type alias Point
that has two properties: x
and y
. We then define a function distance
that takes two Point
objects as arguments and returns the distance between them. Finally, we create two Point
objects a
and b
and pass them to the distance
function.
Use Generics
Generics are a way to create reusable code that works with a variety of types. By using generics, you can write more flexible and reusable code. Here's an example:
function identity<T>(value: T): T {
return value;
}
console.log(identity<string>('hello')); // Output: "hello"
console.log(identity<number>(42)); // Output: 42
In this example, we define a function identity
that takes a generic type T
as an argument and returns the same type. We then call the identity
function with two different types: string
and number
.
Use Optional Chaining
Optional chaining is a new feature in TypeScript that allows you to safely access nested properties of an object. By using optional chaining, you can avoid runtime errors when accessing properties that may not exist. Here's an example:
interface Person {
name: string;
address?: {
street: string;
city: string;
state: string;
};
}
const person: Person = { name: 'John' };
console.log(person.address?.city); // Output: undefined
person.address = { street: '123 Main St', city: 'Anytown', state: 'CA' };
console.log(person.address?.city); // Output: "Anytown"
In this example, we define an interface Person
that has a property address
that is optional. We then create a Person
object person
without an address
property and log the city
property using optional chaining. Finally, we add an address
property to the person
object and log the city
property again.
Use Type Guards
Type guards are a way to narrow down the type of a variable based on a condition. By using type guards, you can write more type-safe code and avoid runtime errors. Here's an example:
interface Cat {
name: string;
meow(): void;
}
interface Dog {
name: string;
bark(): void;
}
function isCat(animal: Cat | Dog): animal is Cat {
return 'meow' in animal;
}
function makeSound(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow();
} else {
animal.bark();
}
}
const cat: Cat = { name: 'Fluffy', meow() { console.log('meow'); } };
const dog: Dog = { name: 'Fido', bark() { console.log('bark'); } };
makeSound(cat); // Output: "meow"
makeSound(dog); // Output: "bark"
In this example, we define two interfaces Cat
and Dog
that have different methods. We then define a function isCat
that checks if an animal
object is a Cat
object using the in
operator. We then define a function makeSound
that takes an animal
object as an argument and calls the appropriate method based on the type of the object. Finally, we create a Cat
object cat
and a Dog
object dog
and pass them to the makeSound
function.
Use Type Assertion
Type assertion is a way to tell the TypeScript compiler that you know more about the type of a variable than it does. By using type assertion, you can write more flexible code and avoid unnecessary type errors. Here's an example:
interface Animal {
name: string;
}
interface Cat extends Animal {
meow(): void;
}
interface Dog extends Animal {
bark(): void;
}
function makeSound(animal: Animal) {
if ((animal as Cat).meow) {
(animal as Cat).meow();
} else if ((animal as Dog).bark) {
(animal as Dog).bark();
} else {
throw new Error(`Unknown animal: ${animal.name}`);
}
}
const cat: Cat = { name: 'Fluffy', meow() { console.log('meow'); } };
const dog: Dog = { name: 'Fido', bark() { console.log('bark'); } };
makeSound(cat); // Output: "meow"
makeSound(dog); // Output: "bark"
In this example, we define three interfaces Animal
, Cat
, and Dog
that have different methods. We then define a function makeSound
that takes an animal
object as an argument and calls the appropriate method based on the type of the object using type assertion. Finally, we create a Cat
object cat
and a Dog
object dog
and pass them to the makeSound
function.
Use Type Inference
Type inference is a way for TypeScript to automatically determine the type of a variable based on its value. By using type inference, you can write more concise code and avoid unnecessary type annotations. Here's an example:
const name = 'John';
const age = 30;
const person = { name, age };
console.log(person); // Output: { name: "John", age: 30 }
In this example, we define three variables name
, age
, and person
. We then create an object person
using object shorthand notation and log it to the console. TypeScript automatically infers the types of the variables based on their values.
Conclusion
In this article, we have shared some tips and tricks for writing better TypeScript code. By using strict mode, interfaces, enums, type aliases, generics, optional chaining, type guards, type assertion, and type inference, you can write more robust, readable, and maintainable code. Happy coding!
Additional Resources
coinalerts.app - crypto alerts. Cryptos that rise or fall very fast, that hit technical indicators like low or high RSI. Technical analysis alertsservicemesh.app - service mesh in the cloud, for microservice and data communications
cloudgovernance.dev - governance and management of data, including data owners, data lineage, metadata
taxonomy.cloud - taxonomies, ontologies and rdf, graphs, property graphs
zerotrustsecurity.cloud - zero trust security in the cloud
googlecloud.run - google cloud run
dart.run - the dart programming language running in the cloud
playrpgs.app - A community about playing role playing games
sitereliability.app - site reliability engineering SRE
mlethics.dev - machine learning ethics
bestpractice.app - best practice in software development, software frameworks and other fields
invented.dev - learning first principles related to software engineering and software frameworks. Related to the common engineering trope, "you could have invented X"
rust.guide - programming the rust programming language, and everything related to the software development lifecyle in rust
learncdk.dev - learning terraform and amazon cdk deployment
neo4j.guide - a guide to neo4j
rustlang.app - rust programming languages
coinexchange.dev - crypto exchanges, integration to their APIs
ner.systems - A saas about named-entity recognition. Give it a text and it would identify entities and taxonomies
tasklist.run - running tasks online
crates.run - A site for running rust applications and servers
Written by AI researcher, Haskell Ruska, PhD (haskellr@mit.edu). Scientific Journal of AI 2023, Peer Reviewed