Sentiment Analysis Using TypeScript Without Dependencies

Sentiment analysis is usually a task that requires a specialised dataset and machine-learning techniques to implement properly. However, I thought it might be a nice exercise to try and implement sentiment analysis with TypeScript without training models.

// Define a type for the sentiment result
type Sentiment = 'positive' | 'neutral' | 'negative';

// Class to perform sentiment analysis on a given text
class SentimentAnalysis {
  // Arrays of positive and negative words to use in the analysis
  private positiveWords = ["love", "like", "great", "good", "happy", "awesome"];
  private negativeWords = ["hate", "dislike", "bad", "angry", "sad", "terrible"];

  // Method to perform sentiment analysis on a given text
  public getSentiment(text: string): { sentiment: Sentiment, positiveWords: number, negativeWords: number, neutralWords: number } {
    // Convert the text to lowercase to make the analysis case-insensitive
    const lowerText = text.toLowerCase();

    // Sum up the number of times each positive word appears in the text
    let positiveScore = this.positiveWords.reduce((acc, word) => {
      // Use a regular expression to match the word in the text
      return acc + (lowerText.match(new RegExp(word, 'g')) || []).length;
    }, 0);

    // Sum up the number of times each negative word appears in the text
    let negativeScore = this.negativeWords.reduce((acc, word) => {
      // Use a regular expression to match the word in the text
      return acc + (lowerText.match(new RegExp(word, 'g')) || []).length;
    }, 0);

    // Calculate the number of neutral words by subtracting the positive and negative words from the total number of words
    let neutralScore = lowerText.split(' ').length - positiveScore - negativeScore;

    // Compare the number of positive and negative words and return the sentiment result
    if (positiveScore > negativeScore) {
      return { sentiment: "positive", positiveWords: positiveScore, negativeWords: negativeScore, neutralWords: neutralScore };
    } else if (positiveScore < negativeScore) {
      return { sentiment: "negative", positiveWords: positiveScore, negativeWords: negativeScore, neutralWords: neutralScore };
    } else {
      return { sentiment: "neutral", positiveWords: positiveScore, negativeWords: negativeScore, neutralWords: neutralScore };
    }
  }
}

// Create an instance of the SentimentAnalysis class
const sentimentAnalysis = new SentimentAnalysis();

// Analyze some sample text
const result = sentimentAnalysis.getSentiment("I love this code and think it is great!");

// Log the result
console.log(result);

This code uses a class called SentimentAnalysis to perform sentiment analysis on a given text. The class has two arrays of positive and negative words and a method called getSentiment that performs the analysis.

The method first converts the text to lowercase and then uses two calls to reduce, to sum up, the number of times each positive and negative word appears in the text. It then calculates the number of neutral words by subtracting the positive and negative words from the total number of words. Finally, it compares the number of positive and negative words and returns a result that includes the sentiment and the number of positive, negative, and neutral words found.

While this code isn’t scientific because it doesn’t perform a deep analysis beyond breaking up a string into words, nor does it account for all positive, negative and neutral words, it’s a cool little implementation you could use for basic purposes.

Testing our code

Because testing is very important and provides a great way to understand code, we will write some Jest unit tests for our sentiment analysis. It’s a basic implementation, but writing tests is a good habit to get into.

import SentimentAnalysis from "./sentiment-analysis";

describe("SentimentAnalysis", () => {
  let sentimentAnalysis: SentimentAnalysis;

  beforeEach(() => {
    sentimentAnalysis = new SentimentAnalysis();
  });

  test("should return positive sentiment for positive text", () => {
    const result = sentimentAnalysis.getSentiment("I love this code and think it is great!");
    expect(result).toEqual({ sentiment: "positive", positiveWords: 2, negativeWords: 0, neutralWords: 7 });
  });

  test("should return negative sentiment for negative text", () => {
    const result = sentimentAnalysis.getSentiment("I hate this code and think it is terrible!");
    expect(result).toEqual({ sentiment: "negative", positiveWords: 0, negativeWords: 2, neutralWords: 7 });
  });

  test("should return neutral sentiment for neutral text", () => {
    const result = sentimentAnalysis.getSentiment("This code is just okay.");
    expect(result).toEqual({ sentiment: "neutral", positiveWords: 0, negativeWords: 0, neutralWords: 3 });
  });
});

Our test cases are basic, but we check that we return the three different types of sentiment from our code. We aren’t handling invalid values or other edge cases you might have in a proper bunch of tests.