The full source code for the ReactSparkPortfolio is available on GitHub

Adding Weather to My Profile Application: A TypeScript Learning Journey

Demonstrating TypeScript in Real-World Applications

As I work on the project, GitHub pages will be the place to view the application: ReactSparkPortfolio on GitHub Pages

As a developer on a learning path with TypeScript, one of my goals is to build meaningful, real-world applications while demonstrating key TypeScript skills. My Profile Application serves as a sandbox for practicing and refining my React Native skills, specifically focusing on TypeScript. Recently, I decided to enrich this project by adding a Weather Forecast feature, which includes fetching live weather data and displaying a map of the user’s location.

Why TypeScript?

TypeScript is a superset of JavaScript that adds static types. It catches errors at compile time, helping avoid runtime failures, especially useful in large, complex applications. This strict control over data types and structure makes TypeScript invaluable for handling data-fetching operations and managing state effectively in React Native apps.

Setting the Scene: The Profile Application

My Profile Application is a project where I display personal details, hobbies, and now, weather forecasts. Integrating weather forecasts allows me to demonstrate TypeScript's role in data-fetching, state management, and UI component creation, such as mapping user locations.

TypeScript Skills Demonstrated

The Weather Forecast and Map components demonstrate important TypeScript concepts:

  • Typed React Components
  • Interface Definitions for Props
  • Strongly Typed State Management
  • Type-Safe Event Handling
  • Handling Asynchronous Data Fetching
  • Type Checking for Conditional Rendering
  • Error Handling and Debugging
1. Typed React Components

React components can be typed using React.FC (Functional Component) to ensure that the component accepts and manages the right props. In my Weather Forecast component, I used React.FC to define a functional component structure with TypeScript, ensuring that I get proper type inference for props and state.

const WeatherForecast: React.FC = () => {
  // Component logic here
}

Using React.FC in TypeScript gives me type safety for props and ensures that I avoid common pitfalls such as forgetting required props or passing the wrong data types into components. This is a foundational skill for building robust, maintainable React Native applications.

2. Interface Definitions for Props

When building reusable components, defining interfaces for props ensures that you pass the correct types of data. For example, the MapComponent needs latitude and longitude values, so I created a MapComponentProps interface to specify the type of these props:

Defining interfaces for component props ensures type safety. The MapComponent, for example, receives latitude and longitude values:

interface MapComponentProps {
  latitude: number;
  longitude: number;
}

const MapComponent: React.FC<MapComponentProps> = ({ latitude, longitude }) => {
  return (
    <MapContainer center={[latitude, longitude]} zoom={13}>
      <Marker position={[latitude, longitude]} />
    </MapContainer>
  );
};

By defining this interface, I make sure that the MapComponent always receives numeric values for latitude and longitude. If someone tries to pass in a string or other invalid data type, TypeScript will immediately throw an error during development, helping me avoid bugs before they happen.

3. Strongly Typed State Management

In the WeatherForecast component, I used useState hooks with explicit type annotations to ensure that the state variables are strongly typed. This is particularly important when handling different kinds of data, such as user input, fetched weather data, or UI states like loading indicators.

const [city, setCity] = useState<string>('');
const [loading, setLoading] = useState<boolean>(false);
const [weatherData, setWeatherData] = useState<any>(null);
const [error, setError] = useState<string>('');

This explicit type declaration ensures that these variables only store data of the expected type. For example, city can only be a string, and trying to assign any other type will raise a TypeScript error, preventing type-related bugs early in development.

4. Type-Safe Event Handling

Capturing and responding to user input in forms is a common task in React applications. By typing event handlers in TypeScript, I ensure that the form behaves predictably. Here’s how I typed the event in the input field for entering the city name:

<Form.Control
  type="text"
  placeholder="Enter city"
  value={city}
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => setCity(e.target.value)}
/>

TypeScript ensures that the event is correctly typed as React.ChangeEvent<HTMLInputElement>, which means the function knows exactly what kind of event to expect. This prevents issues like mistakenly accessing properties that don’t exist on the event object.

5. Handling Asynchronous Data Fetching

The Weather Forecast feature involves fetching data from a weather API. I used async/await in combination with TypeScript's strong typing to handle the API response and possible errors.

const fetchWeather = async () => {
  setLoading(true);
  setError('');

  try {
    const response = await fetch(`https://api.example.com/weather?location=${city}`);
    const data = await response.json();

    if (data.success) {
      setWeatherData(data);
    } else {
      setError('Failed to fetch weather data.');
    }
  } catch (err: any) {
    setError(`Error: ${err.message}`);
  } finally {
    setLoading(false);
  }
};

Handling asynchronous operations properly is a critical skill in web development. By typing the error as any (catch (err: any)), I ensure that any error object caught will be properly typed and can be inspected. The result is clear, concise error handling, with informative messages passed back to the user when something goes wrong. TypeScript ensures that I handle the API response and error cases correctly, making the code more robust and reliable. By explicitly typing the error object as any, I can access its properties without TypeScript complaining about unknown types.

6. Type Checking for Conditional Rendering

When dealing with fetched data, conditional rendering is crucial to ensure that the app doesn’t attempt to render undefined or null values. In the Weather Forecast component, I only render the forecast and map if the weatherData is available.

{weatherData && (
  <>
    <Card>...Weather Details...</Card>
    <MapComponent
      latitude={weatherData.location.latitude}
      longitude={weatherData.location.longitude}
    />
  </>
)}

By adding this conditional check, TypeScript ensures that I won’t accidentally try to access properties of weatherData before it’s been set, preventing potential runtime errors from undefined values.

7. Error Handling and Debugging

One of the key advantages of TypeScript is that it forces you to think about edge cases like errors or missing data. In the Weather Forecast component, I handle errors both in the API request and in user interaction (e.g., if no city is entered or the API call fails). This improves the user experience by displaying meaningful error messages when things go wrong.

{error && <div className="alert alert-danger">{error}</div>}

TypeScript helps ensure that all errors are properly handled. With clear types, it’s easier to trace where an error comes from, making debugging much more efficient.

Demonstrating My TypeScript Growth

Through the development of the Weather Forecast and Map components, I have sharpened several important TypeScript skills, particularly in building robust, type-safe React components. Here’s a summary of the key skills demonstrated:

  • Typed components using React.FC to ensure type safety for functional components.
  • Interfaces for defining prop types, ensuring reusable components receive the correct data structure.
  • Strongly typed state management with useState, ensuring that state variables only hold values of the expected type.
  • Type-safe event handling to capture user input without runtime errors.
  • Asynchronous data fetching with proper error handling, ensuring robustness when dealing with external APIs.
  • Type checking for conditional rendering, ensuring the app doesn’t attempt to render undefined or null values.
  • Clear error handling to improve the user experience with informative feedback.
What’s Next?

With the weather and map components successfully integrated into my Profile Application, I’m eager to continue exploring more advanced TypeScript topics, such as custom hooks, generic types, and state management libraries like Redux. Each new feature provides another opportunity to refine my TypeScript skills while building something tangible.

By making small but meaningful improvements to my application, I can demonstrate the practical benefits of TypeScript, not just in theory but in real-world scenarios that other developers can appreciate and learn from.