한국어

From JS to TS

JavaScript was created in 1995 by Brendan Eich in just 10 days. The original goal was simple: to add basic interactivity to web pages, like popping up a message when a button is clicked.

The problem is that the web grew far faster and bigger than anyone expected. With the arrival of Node.js in 2009, JavaScript expanded to the server side, and SPA frameworks like Angular, React, and Vue exploded in popularity, increasing front-end codebases to hundreds of thousands of lines. Yet JavaScript’s design remained essentially that of "a 10-day scripting language."

 
 
js
function getUser(id) {
  // No one knows if id is a number, string, or undefined
}

getUser()       // runs fine
getUser("abc")  // runs fine
getUser({})     // runs fine — but crashes at runtime 💀

Eventually, Microsoft provided a solution in 2012: TypeScript, designed by Anders Hejlsberg, the creator of C#. Its core philosophy was simple.

"It doesn’t replace JS. It just adds types on top of JS."

When Angular 2 officially adopted TypeScript in 2016, it rapidly spread in enterprise projects. VS Code, built itself using TypeScript, became the industry-standard editor, making the difference in developer experience clear. Today, TypeScript consistently ranks among the "most loved languages" in Stack Overflow’s developer surveys.

This article aims to introduce the core TypeScript syntax from a practical perspective for developers already using JavaScript.


1. Basic Type Declarations: Adding Types to Variables and Functions

Getting started with TypeScript is simple: explicitly declare types on variables and functions.

According to the official TypeScript documentation, TypeScript includes all JavaScript features and adds a type system layer on top. Existing JS code runs unchanged in TS.

 
 
ts
// Declaring variable types
let username: string = "Alice";
let age: number = 30;
let isAdmin: boolean = false;

// Declaring function parameter and return types
function greet(name: string): string {
  return `Hello, ${name}!`;
}

greet(123); // Compile error — number can't be assigned to string
greet("Bob"); // Valid

In plain JS, greet(123) would run and return "Hello, 123!". TS catches this mistake at coding time.

Type inference means you don’t always have to explicitly write types: TypeScript automatically infers a variable’s type when assigned.

 
 
ts
let count = 10; // TypeScript infers number
count = "ten";   // Compile error — already inferred as number

2. Interface and Type: Defining Object Shapes

In real projects, you often deal with objects rather than simple primitives. TypeScript lets you define object structures using two main ways: interfaces and types.

The official TypeScript docs present interfaces and types as two primary ways to create complex types. Generally, interfaces are recommended for object shapes; types are reserved for special use cases.

 
 
ts
// Define object structure with an interface
interface User {
  id: number;
  name: string;
  email: string;
  role?: string; // ? means optional property
}

function displayUser(user: User): void {
  console.log(`${user.name} (${user.email})`);
}

const alice: User = { id: 1, name: "Alice", email: "alice@example.com" };
displayUser(alice); // Valid

const broken = { id: 2, name: "Bob" }; // missing email
displayUser(broken); // Compile error — email property missing

Types are often used to represent complex combinations like unions beyond just objects.

 
 
ts
// Union type — allows multiple types
type ID = number | string;

let userId: ID = 101;
userId = "user-abc"; // Both allowed

// Literal type — only specific values allowed
type Status = "pending" | "approved" | "rejected";

function updateStatus(status: Status): void {
  console.log(`Status changed to: ${status}`);
}

updateStatus("approved");  // Valid
updateStatus("cancelled"); // Compile error — not allowed

3. Generics: Treating Types Like Parameters

Generics may feel unfamiliar at first, but once understood, they reveal TypeScript’s true power: writing reusable code while maintaining type safety.

For example, imagine creating a function to handle API responses.

 
 
ts
// Using any loses type safety
function fetchData(url: string): any {
  // ...
}

// Using Generics allows specifying return type at call time
async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url);
  return response.json();
}

interface Post {
  id: number;
  title: string;
}

// Specify type when calling
const post = await fetchData<Post>("/api/post/1");
console.log(post.title); // Auto-complete + type checking
console.log(post.author); // Compile error: Post has no author

<T> is a type parameter. When calling the function, you provide the desired type for T, and TypeScript checks the entire function against it. Since using any disables this checking, generics are a robust alternative.


4. strictNullChecks: Handling null and undefined

One of the most common JavaScript errors is "Cannot read properties of undefined." TypeScript’s strictNullChecks option prevents this at compile time.

According to the official TypeScript documentation, setting strictNullChecks to true treats null and undefined as distinct types, causing errors if used like regular values.

 
 
ts
// With strictNullChecks: true

const users = ["Alice", "Bob", "Charlie"];
const found = users.find(u => u === "Dave"); // string | undefined

console.log(found.toUpperCase()); // Error: found may be undefined

// Solution 1: Optional chaining
console.log(found?.toUpperCase()); // returns undefined if found is undefined

// Solution 2: Type guard
if (found) {
  console.log(found.toUpperCase()); // here, found is a string
}

TypeScript forces you to explicitly recognize and handle "this value might be absent," catching these issues during development instead of at runtime.


Conclusion

TypeScript is not a new language replacing JavaScript; it’s a tool designed to solve problems that arise as JS codebases and teams grow. Initially, adding types to every variable may seem tedious. But once you experience VS Code catching errors in real-time, enjoy precise auto-completion, and see your teammates’ code become self-documenting, you’ll realize type declarations are an investment, not a cost.

← Back to blog