TypeScript Introduction

TypeScript is an open-source programming language developed by Microsoft. It is a superset of JavaScript that, while fully compatible with JavaScript syntax, adds an optional static type system and class-based object-oriented programming capabilities.

TypeScript code is ultimately compiled to pure JavaScript and can run in any environment that supports JavaScript, including browsers, Node.js, and mobile platforms.


Why TypeScript?

JavaScript is a dynamically typed language; the type of a variable can only be determined at runtime.

This flexibility works well in small projects, but as project scale grows, problems gradually surface:

Problem ScenarioJavaScript's DilemmaTypeScript's Solution
Passing wrong type to function parameterErrors only appear at runtime, hard to detect earlyErrors appear at compile time, with real-time IDE hints
Accessing non-existent propertiesReturnsundefined, behavior is unpredictableThe compiler directly reports an error and refuses to pass it
Refactoring large projectsChange one place, no idea where it will breakThe type system automatically tracks all references
Team collaborationWhat parameters a function accepts and what it returns relies entirely on comments or documentationType signatures serve as documentation, with IDE autocompletion
Code readabilityYou cannot directly know the data structure from a function definitionInterfaces and type aliases make the data structure clear at a glance

TypeScript's essential goal is not to replace JavaScript, but to make large-scale JavaScript projects manageable and maintainable. It is JavaScript's "safety net", not a competitor.

Let's look at the most intuitive example:

Example

// JavaScript: error occurs only at runtime
function greet(name) {
    return "Hello, " + name.toUpperCase();
}

greet(123); // Runtime error: name.toUpperCase is not a function

Example

// TypeScript: errors can be found at compile time
function greet(name: string): string {
    return "Hello, " + name.toUpperCase();
}

greet(123); // Compilation error: Argument of type 'number' is not assignable to parameter of type 'string'
greet("example"); // Correct: Hello, EXAMPLE

Relationship between TypeScript and JavaScript

The relationship between TypeScript and JavaScript can be summarized in one sentence:TypeScript is a superset of JavaScript, meaning all valid JavaScript code is also valid TypeScript code.

Set Relationship Illustration

JavaScript (all JS code)
⊂ TypeScript (JS + type system + new features)
⊂ Compiled output (standard JavaScript, can run in any environment)

Core differences between the two:

Comparison ItemJavaScriptTypeScript
Type systemDynamic typing, determined at runtimeStatic typing, checked at compile time (optional)
Execution methodRun directly in browser / Node.jsMust be compiled to JS before running
Error detection timingRuntimeCompile time (found early)
IDE supportBasic completionStrong type inference, precise completion, refactoring support
Learning curveRelatively gentleRequires additional learning of type system
Compatibility with existing JS—Fully compatible, can migrate gradually
File extension.js.tsor.tsx(including JSX)

TypeScript supports "progressive adoption": you don't have to rewrite the entire project at once. You can first.jschange.ts, then gradually add types to key modules, and existing code continues to run as usual.


Core Features

TypeScript introduces a complete type system on top of JavaScript. The following are the most commonly used core features.

Basic Type Annotations

Declare types after variables, function parameters, and return values using a colon; this is the most basic syntax of TypeScript.

Example

// Basic types: number, string, boolean, null, undefined, symbol, bigint
let age: number = 25;
let username: string = "example";
let isActive: boolean = true;

// Array types, two equivalent ways
let scores: number[] = [90, 85, 92];
let tags: Array<string> = ["typescript", "javascript"];

// Tuple: array with fixed length and types
let point: [number, number] = [10, 20];
let entry: [string, number] = ["EXAMPLE", 100];

// Function: parameter types + return type
function add(a: number, b: number): number {
    return a + b;
}

// void: function has no return value
function log(msg: string): void {
    console.log(msg);
}

// Optional parameter: add ? after the parameter name
function greet(name: string, title?: string): string {
    return title ? `${title} ${name}` : name;
}

console.log(greet("EXAMPLE"));          // Output: EXAMPLE
console.log(greet("example", "Mr."));  // Output: Mr. example

Interface

An interface is used to describe the "shape" of an object, defining which properties and methods the object should have.

Example

// Define interface: describe the structure of a user object
interface User {
    id: number;          // Required
    name: string;        // Required
    email?: string;      // Optional, marked with ?
    readonly role: string; // Read-only, cannot be modified after assignment
}

// Function parameter uses interface type
function printUser(user: User): void {
    console.log(`ID: ${user.id}, Name: ${user.name}`);
    if (user.email) {
        console.log(`Email: ${user.email}`);
    }
}

const admin: User = {
    id: 1,
    name: "EXAMPLE",
    email: "example@example.com",
    role: "admin",
};

printUser(admin);
// Output: ID: 1, Name: EXAMPLE
// Output: Email: example@example.com

// admin.role = "user"; // Error: Cannot assign to 'role' because it is a read-only property.

// Interface inheritance
interface AdminUser extends User {
    permissions: string[];
}

Type Alias

Type aliases usetypethe keyword to create, and can define aliases for any type, including complex structures such as union types and intersection types.

Example

// Union type: variable can be one of multiple types
type ID = string | number;

let userId: ID = "abc-123"; // Valid
userId = 456;               // Also valid

// Literal type: restricts variable to specific values
type Direction = "up" | "down" | "left" | "right";
type Status = "pending" | "active" | "inactive";

function move(dir: Direction): void {
    console.log(`Moving ${dir}`);
}

move("up");    // Correct
// move("diagonal"); // Error: not within the literal range

// Intersection type: combines multiple types
type WithTimestamp = {
    createdAt: Date;
    updatedAt: Date;
};

type UserRecord = User & WithTimestamp; // Has all properties of both User and WithTimestamp

// Function type alias
type Transformer<T, U> = (input: T) => U;
const toNumber: Transformer<string, number> = (s) => parseInt(s, 10);

Generics

Generics allow writing reusable components while maintaining type safety. You can think of generics as "type variables"—pass in specific types when using them.

Example

// Without generics: can only handle number type
function firstNumber(arr: number[]): number {
    return arr[0];
}

// Using generics: T is a type parameter, the specific type is determined at call time
function first<T>(arr: T[]): T {
    return arr[0];
}

const n = first<number>([1, 2, 3]);    // n is of type number
const s = first<string>(["a", "b"]);  // s is of type string
const inferred = first([true, false]); // TypeScript automatically infers T as boolean

// Generic interface
interface ApiResponse<T> {
    data: T;
    status: number;
    message: string;
}

// Using generic interface
const userResponse: ApiResponse<User> = {
    data: { id: 1, name: "EXAMPLE", role: "admin" },
    status: 200,
    message: "success",
};

// Generic constraint: T must have an id property
function getById<T extends { id: number }>(items: T[], id: number): T | undefined {
    return items.find(item => item.id === id);
}

Enum

Enums are used to define a set of named constants, making "magic numbers" or "magic strings" in code meaningful.

Example

// Numeric enum (default increments from 0)
enum Direction {
    Up,     // 0
    Down,   // 1
    Left,   // 2
    Right,  // 3
}

console.log(Direction.Up);    // Output: 0
console.log(Direction[0]);    // Output: "Up" (reverse mapping)

// String enum (recommended, values are more readable when debugging)
enum Color {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE",
}

function paint(color: Color): void {
    console.log(`Painting in ${color}`);
}

paint(Color.Red); // Output: Painting in RED
// paint("red"); // Error: string "red" is not of type Color

// const enum: inlined after compilation, reducing runtime overhead
const enum HttpStatus {
    OK = 200,
    NotFound = 404,
    InternalError = 500,
}

const status: HttpStatus = HttpStatus.OK; // Replaced directly with the number 200 after compilation

Type Inference

TypeScript doesn't require manually annotating types for every variable; the compiler automatically infers them based on the assigned value.

Example

// TypeScript automatically infers variable types, no explicit annotation needed
let count = 0;            // Inferred as number
let name = "EXAMPLE";      // Inferred as string
let flag = true;          // Inferred as boolean

// Inferred array element type
let numbers = [1, 2, 3];  // Inferred as number[]

// Infer function return type
function double(n: number) {  // Return type auto-inferred as number
    return n * 2;
}

// Object literal inference
const config = {
    host: "localhost",    // string
    port: 3000,           // number
    debug: false,         // boolean
};

// config.port = "3000"; // Error: Type 'string' is not assignable to type 'number'.

A practical suggestion: function parameters usually need explicit type annotations (because callers can't infer types from parameter values), while local variables and return values inside the function body can mostly rely on inference—no need to annotate everything manually.

Classes and Access Modifiers

TypeScript fully supports ES6 class syntax and addspublic、private、protected、readonlyfour access modifiers.

Example

class Animal {
    readonly name: string;         // Readonly, cannot be modified after initialization
    private age: number;           // Private, accessible only inside the class
    protected species: string;     // Protected, accessible by subclasses

    constructor(name: string, age: number, species: string) {
        this.name = name;
        this.age = age;
        this.species = species;
    }

    // public method (public by default, can be omitted)
    public introduce(): string {
        return `I'm ${this.name}, a ${this.species}.`;
    }

// getter: invoke a method like accessing a property
    get info(): string {
        return `${this.name} (${this.age} years old)`;
    }
}

class Dog extends Animal {
    private breed: string;

    constructor(name: string, age: number, breed: string) {
super(name, age, "Canis lupus familiaris"); // Call the parent class constructor
        this.breed = breed;
    }

// Subclass can access protected properties
    describe(): string {
        return `${this.name} is a ${this.species}, breed: ${this.breed}`;
    }
}

const dog = new Dog("EXAMPLE", 3, "Labrador");
console.log(dog.introduce()); // Output: I'
m EXAMPLE, a Canis lupus familiaris.
console.log(dog.info);         // Output: EXAMPLE (3 years old)
// console.log(dog.age); // Error: private property cannot be accessed from outside

Decorator

Decorators are a metaprogramming syntax used to attach extra behavior to classes, methods, and properties; commonly seen in frameworks like Angular, NestJS, etc.

TypeScript 5.0 has implemented decorators as an official standard (Stage 3 proposal), no longer requiring theexperimentalDecoratorsflag.

Example

// Class decorator: executed when the class is defined
function sealed(constructor: Function) {
    Object.seal(constructor);         // Prevent adding new properties
    Object.seal(constructor.prototype);
}

// Method decorator: can be used for logging, permission checks, etc.
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`Calling ${propertyKey} with args:`, args);
        const result = original.apply(this, args);
        console.log(`${propertyKey} returned:`, result);
        return result;
    };
}

@sealed
class Calculator {
    @log
    add(a: number, b: number): number {
        return a + b;
    }
}

const calc = new Calculator();
calc.add(1, 2);
// Output: Calling add with args: [ 1, 2 ]
// Output: add returned: 3

Application Areas

Thanks to its full compatibility with JavaScript, TypeScript can be used almost everywhere JavaScript runs, and brings additional benefits in large projects.

Front-end Web Development

This is the most widely used scenario for TypeScript. All three major frontend frameworks provide first-class support for TypeScript:

FrameworkTypeScript SupportTypical Scenario
AngularOfficial language, TypeScript by default, cannot be avoided.Enterprise SPA, admin systems
ReactOfficially provided@types/react, Create React App and Vite both include TypeScript templatesE-commerce, content platforms, admin/backend applications
VueVue 3 core code is rewritten in TypeScript, and the Composition API significantly improves TypeScript friendlinessSmall to medium projects, progressive migration
Next.js / NuxtBuilt-in TypeScript support, enabled by default for new projectsFull-stack SSR/SSG applications

Back-end Node.js Development

TypeScript is equally prevalent in Node.js backend development.NestJSIt is currently the most popular TypeScript backend framework, adopting a modular architecture similar to Angular, with built-in dependency injection and decorator support.

In addition,DenoThe runtime natively supports TypeScript from day one, and can run directly without any configuration.tsfiles.BunAlso has built-in TypeScript support.

Command-line Tools and Scripts

With the help ofts-node、tsxand other tools, TypeScript code can be executed directly without precompilation, making it ideal for writing build scripts and CLI tools.

Mobile and Cross-platform

React NativeFully supports TypeScript, and many enterprise mobile applications (such as Microsoft Office mobile, Shopify, etc.) use this technology stack.ExpoThe new project template is also based on TypeScript by default.

Game Development and Graphics

Babylon.js(Microsoft's 3D engine) is written entirely in TypeScript and treats TypeScript type definitions as first-class citizens.Phaserand other 2D game engines also provide complete type definitions.


Development History

The birth and growth of TypeScript are closely related to the evolution of the JavaScript ecosystem, and every major version release is driven by clear engineering needs.

Origins (2010 - 2012)

TypeScript's story began with an engineering challenge inside Microsoft: how to make large JavaScript projects maintainable.

DesignerAnders Hejlsbergis an industry legend—the chief architect of C# and the creator of Turbo Pascal and Delphi. He started designing this language around 2010, with the core goal of introducing static type checking without breaking JavaScript compatibility.

Another background for Microsoft's launch of TypeScript was the release of Windows 8—applications could be developed using HTML + JavaScript, and Microsoft hoped to attract .NET programmers, so many of TypeScript's syntax features were intentionally kept similar to C# and .NET.

In October 2012, TypeScript 0.8 was publicly released, after about two years of internal development at Microsoft.

Early Versions (2013 - 2015)

TimeVersionImportant Events
2013-060.9Official stable release, greatly improved performance, better IDE integration
2014-041.0First official stable version, introducing classes, interfaces, modules, and supporting compilation to standard JavaScript
2014-07—TypeScript compiler source code open-sourced, hosted on GitHub, accepting community contributions
2015-04—Microsoft releases Visual Studio Code with deep built-in TypeScript support; the two complement each other
2015-071.5Introduced ES6 module syntax support, decorators (experimental), and namespaces

Rapid Growth (2016 - 2019)

This stage was a critical period for TypeScript to move from a niche tool to the mainstream, with the core driving force coming from choices in the framework ecosystem.

TimeVersion / EventMilestone significance
2016-09TypeScript 2.0Introduced non-nullable types (--strictNullChecks), one of the most important features of TypeScript type safety; introduced discriminated union types
2016Angular 2 releasedThe Google Angular team announced adopting TypeScript as the official development language, bringing TypeScript into the public eye
2017TypeScript 2.x seriesConditional types, mapped types, and the infer keyword were introduced successively, greatly enhancing the expressiveness of the type system
2018-07TypeScript 3.0Project References, supporting large monorepos; introduced the unknown type (a safer top type than any)
2019DefinitelyTypedCommunity-maintained type definition repository DefinitelyTyped (@types/*) covers over 7,000 packages, basically covering all mainstream npm libraries

Maturity and Adoption (2020 - 2022)

TimeVersion / EventImportant content
2020-08TypeScript 4.0Variadic tuple types, labeled tuple elements; significantly improved editor experience
2021TypeScript 4.x seriesTemplate Literal Types, enhanced built-in utility types,noImplicitOverrideand other strict mode enhancements
2022State of JS surveyTypeScript usage rate surpassed pure JavaScript for the first time, becoming the most popular superset language in the JS ecosystem
2022Vue 3 / Vite adoptionVue 3 core rewritten in TypeScript, combined with Vite to bring the TypeScript experience in front-end engineering to a new level

Modern Era (2023 - Present)

TimeVersionKey features
2023-03TypeScript 5.0Modern decorators (Stage 3 standard), const type parameters, all enums upgraded to union enums, with significant performance improvements
2023-08TypeScript 5.2Introduced Explicit Resource Management (usingkeyword), solving the problem of deterministic cleanup of resources such as database connections and file handles
2024-03TypeScript 5.4Improved type narrowing in closures,NoInferutility types
2024-06TypeScript 5.5type predicate inference, regular expression syntax checking, making the type system understand code logic more precisely
2024-11TypeScript 5.7Support for--target es2024, more reliable detection of uninitialized variables
2025-03TypeScript 5.8Enhanced return value checking for conditional and indexed access types; supports under--module nodenextviarequire()loading ESM modules (requires Node.js 22+)

Future Outlook: Rewriting the Compiler in Go

In early 2025, Microsoft announced a major decision: rewriting the TypeScript compiler in Go, with the project code nameCorsa。

The official statement says the rewritten compiler is expected to deliver a performance improvement of more than 10x, reducing build times for large projects from minutes to seconds.

According to the plan, the Go version will be officially released in TypeScript 7.x, while the current 5.x / 6.x TypeScript versions continue normal iteration and maintenance. The rewrite is expected to be completed by the end of 2025.

This rewrite will not change the syntax or type system of the TypeScript language itself; it is a transparent upgrade for developers—your code does not need any changes, only compilation speed is greatly improved.


Comparison between TypeScript and Other Statically Typed Languages

If you have a background in other strongly typed languages, the following comparison can help you quickly understand TypeScript's design philosophy.

Comparison itemTypeScriptJava / C#Go
Type systemStructural typing (duck typing)Nominal typing (must explicitly declare inheritance relationships)Structural typing (implicit interfaces)
Null safetyEnablingstrictNullCheckssupports it afterwardJava requires annotations or Optional, C# 8+ supportsThrough error values and nil checks
GenericsSupported, syntax similar to Java/C#Supported, Java has type erasure limitationsGo 1.18+ supports it, with simpler syntax
Compiled outputJavaScript (runs in JS environments)Bytecode (runs on JVM / CLR)Native machine code
Runtime type checkingNone (type information is erased after compilation)Yes (reflection mechanism)None
Learning curveFriendly to JS developers, can be adopted incrementallySteeper for beginnersRelatively simple, moderate learning curve

TypeScript usesa structural typing system(Structural Typing): as long as two types have the same "shape", they are considered compatible, without requiring explicit declaration of inheritance relationships. This is fundamentally different from the nominal typing of Java/C#, and is also the key reason TypeScript can interoperate seamlessly with JavaScript.


Limitations of TypeScript

TypeScript is not a silver bullet; understanding its boundaries is equally important.

LimitationDescriptionHow to address
No types at runtimeType information is completely erased after compilation, and types cannot be relied upon for judgments at runtimeUse libraries such aszod、io-tswhen runtime validation is needed
Compilation stepAdds a compilation step compared to pure JS, increasing project complexityModern build tools (Vite, esbuild) already handle this well
any type escape hatchOveruse ofanyrenders type checking ineffectiveEnablenoImplicitAnyandstrictmode, combined with ESLint rules
Type gymnastics barrierComplex conditional types and mapped types are not beginner-friendlyMost business code does not require advanced types; take it step by step
Third-party library supportA few older libraries lack type definitions, requiring you to write .d.ts yourselfFirst check@types/*; if truly absent, write the declaration file manually
Other extensions