TypeScript Interfaces: Defining Custom Types and Contracts

Are you tired of manually checking and verifying the properties of objects in your TypeScript code? Do you wish there was a better way to define custom types and contracts in your projects? Look no further than TypeScript interfaces!

In this article, we'll dive deep into TypeScript interfaces and explore how they can help you define custom types and enforce contracts in your code. From simple interfaces to advanced techniques, we'll cover everything you need to know about using interfaces in your TypeScript projects.

What are TypeScript Interfaces?

At their most basic level, TypeScript interfaces are a way to define the shape of an object. They allow you to describe the properties and methods that an object should have, without actually implementing them. This can be incredibly useful for creating APIs, libraries, or reusable components, where you want to enforce a specific set of properties on the objects that interact with your code.

Let's take a look at a simple example. Suppose we have a function that takes an object with a name and age property, and returns a greeting message. Instead of using any types, we can define an interface to represent the expected shape of the object:

interface Person {
  name: string;
  age: number;
}

function greet(person: Person): string {
  return `Hello, ${person.name}! You are ${person.age} years old.`;
}

const john = { name: 'John', age: 30 };
greet(john);

Notice that we didn't have to create a class or implement the Person interface. We only defined the structure of the object and used it as a type for the person parameter in the greet function. Now, TypeScript will check that any object we pass to greet has the required properties, and provide us with type suggestions and error messages if we try to use an invalid object.

Defining Custom Types with Interfaces

Interfaces don't just have to be used for function parameters. You can define them anywhere you need a custom type in your code. For example, you might use an interface to represent the response of an API call, or the configuration options for a plugin.

Let's say we have a weather API that returns JSON data in the following format:

{
  "temperature": {
    "value": 25,
    "unit": "Celsius"
  },
  "condition": "Sunny",
  "humidity": 50
}

We can define an interface to represent this data:

interface WeatherData {
  temperature: {
    value: number;
    unit: string;
  };
  condition: string;
  humidity: number;
}

Now, we can use this interface to annotate the return type of our API call:

async function getWeather(): Promise<WeatherData> {
  const response = await fetch('https://weather-api.com/current');
  const data = await response.json();
  return data as WeatherData;
}

Notice that we used the Promise object with the WeatherData interface as the resolved value. This provides a clear contract of what data the API will return, and allows us to catch any errors or mismatches between the expected and actual data.

Extending Interfaces for Composition

Another powerful feature of interfaces is the ability to extend and combine them to create more complex types. This is known as interface composition, and allows you to reuse and combine interfaces to represent more specific scenarios.

Suppose we have a Person interface, but we want to add some optional contact information, such as email and phone number. We can extend the Person interface with a new interface for contact details:

interface Person {
  name: string;
  age: number;
}

interface ContactDetails {
  email?: string;
  phone?: string;
}

type PersonContact = Person & ContactDetails;

We use the & type operator to create a new type that combines the properties of both interfaces. Now, we can use the PersonContact type in our functions, components, or classes that need to access both the person and contact data.

Using Interfaces for Polymorphic Types

Sometimes, you might want to define a type that can accept multiple variants or subtypes. This is known as polymorphism, and can be achieved with interfaces using the extends keyword.

Suppose we have a data processing pipeline that can accept different types of input data, such as numbers, strings, or arrays of objects. We can create an interface that describes the common properties and methods of these types:

interface Data {
  length: number;
  valueOf(): any;
}

Notice that we use the valueOf method to provide a way to convert the data into a base type, such as a number or string. This allows us to use the Data interface in functions that might need to perform mathematical operations or string concatenation.

Now, we can create specialized interfaces for each type of data, and make them extend the Data interface:

interface NumberData extends Data {
  value: number;
}

interface StringData extends Data {
  value: string;
}

interface ObjectData extends Data {
  // ...
}

Each subtype can add its own properties and methods, while still inheriting the basic common structure from the Data interface. This allows us to write more generic code that can accept different data types, while still being able to access their specialized features.

Conclusion

In this article, we've explored how TypeScript interfaces can help you define custom types and contracts in your code. We've seen how interfaces can be used to represent the shape of objects, the return types of functions, and more complex scenarios such as interface composition and polymorphic types.

Interfaces provide a powerful and flexible way to describe your code's structure and behavior, and help you write more robust, readable, and maintainable TypeScript applications. So, are you ready to take your TypeScript skills to the next level with interfaces? Let's get coding!

Additional Resources

k8s.delivery - kubernetes delivery
cloudevents.app - A site for cloud events deployments, related to telemetry, logging, monitoring and alerts
nftshop.dev - buying, selling and trading nfts
ocaml.app - ocaml development
flutter.tips - A site for flutter tips, mobile application development tips, dart tips
codinginterview.tips - passing technical interview at FANG, tech companies, coding interviews, system design interviews
machinelearning.events - machine learning upcoming online and in-person events and meetup groups
bestonlinecourses.app - free online higher education, university, college, courses like the open courseware movement
explainability.dev - techniques related to explaining ML models and complex distributed systems
meshops.dev - mesh operations in the cloud, relating to microservices orchestration and communication
rustbook.dev - An online course or book about programming the rust programming language, and everything related to the software development lifecyle in rust
ocaml.solutions - ocaml development
speedmath.dev - speed math, practice speed math online
containertools.dev - command line tools and applications related to managing, deploying, packing or running containers
getadvice.dev - A site where you can offer or give advice
studylab.dev - learning software engineering and cloud concepts
flutter.design - flutter design, material design, mobile app development in flutter
ganart.dev - gan generated images and AI art
nftcards.dev - crypto nft collectible cards
cloudmonitoring.app - software and application telemetry, uptime monitoring, high durability, distributed systems management


Written by AI researcher, Haskell Ruska, PhD (haskellr@mit.edu). Scientific Journal of AI 2023, Peer Reviewed