TypeScript Fundamentals: Type System and Core Concepts

What is TypeScript?

TypeScript is a superset of JavaScript that adds static typing capabilities. While JavaScript is a dynamically-typed language, TypeScript brings compile-time type checking similar to languages like Java or C#.

The key distinction: dynamically-typed languages allow variables to hold any type of value without declaration, whereas statically-typed languages require explicit type specifications when declaring variables.

TypeScript offers two major advantages:

  • Error detection: Type mismatches are caught during development rather than at runtime
  • Intelligent suggestions: IDE autocomplete provides relevant method recommendations

Installation

npm install -g typescript

Verify installation with:

tsc -v

Creating a TypeScript Vue Project

npm create vite

Type Annotations

Type annotations define constraints for variables:

let userAge: number = 25

The :number annotation enforces that userAge can only hold numeric values. Attempting to assign a string or other type results in a compilation error.

TypeScript Type System

Basic Types

TypeScript supports all JavaScript primitive types:

Category Types
Primitives number, string, boolean, null, undefined
Complex arrays, functions, objects

Array Declaration:

let numbers: number[] = []
let names: Array<string> = ['alice', 'bob']

Union Types

Combine multiple types for a single variable:

let data: (number | string)[] = [1, 2, 'hello']

With union types, only methods common to all constituent types are accessible. For example, toString() works because both number and string share this method.

Type Aliases

Create reusable type definitions:

type MixedArray = (number | string)[]

let firstData: MixedArray = [1, 2, 'test']
let secondData: MixedArray = ['a', 'b', 3]

Function Types

Specify parameter and return types:

function calculate(a: number, b: number): number {
    return a * b
}

Function Type Alias:

type MathOperation = (x: number, y: number) => number

const multiply: MathOperation = (x, y) => {
    return x * y
}

Type aliases are primarily used with arrow functions and function expressions rather than function declarations.

Void Type

Use void for functions that don't return a value:

function printMessage(message: string): void {
    console.log(message)
}

While JavaScript functions without explicit returns produce undefined, TypeScript prefers void for such cases.

Optional Parameters

Mark parameters with ? when they're not required:

const greet = (name?: string, id?: number): void => {
    console.log(`Hello ${name}, ID: ${id}`)
}

greet('alice')

Important: Required parameters must precede optional ones.

Object Types

Define object structures inline or with type aliases:

let user: {
    username: string,
    userId: number,
    greet: (msg: string) => void
} = {
    username: 'charlie',
    userId: 42,
    greet(msg) {
        console.log(msg)
    }
}

Using Type Alias:

type User = {
    username: string,
    userId: number,
    greet: (msg: string) => void
}

let admin: User = {
    username: 'admin',
    userId: 1,
    greet(msg) {
        console.log(msg)
    }
}

Interfaces

Interfaces define object contracts:

interface Person {
    name: string
    age: number
    sayHello: () => void
}

const person: Person = {
    name: 'david',
    age: 30,
    sayHello() {
        console.log('Hi')
    }
}

Interface Syntax:

interface InterfaceName {
    propertyName: type
}

Interface vs Type Alias

Aspect Interface Type Alias
Object types
Other types (union, primitive)
Extensibility via extends via & operator

Recommendation: Prefer type aliases when possible due to greater flexibility.

Interface Inheritance

interface Animal {
    species: string
    weight: number
}

interface Dog extends Animal {
    breed: string
    bark: () => void
}

const myDog: Dog = {
    species: 'canine',
    weight: 15,
    breed: 'golden retriever',
    bark() {
        console.log('Woof')
    }
}

Extending Types with Intersection

type Vehicle = {
    wheels: number
}

type Electric = {
    batteryCapacity: number
}

type ElectricVehicle = Vehicle & Electric

The & operator creates an intersection, requiring all properties from both types.

Tuples

Fixed-length arrays with specific types at each position:

let coordinates: [number, number] = [40.7128, -74.0060]

Common use case: geographic coordinates in mapping applications.

Literal Types

Restrict values to specific options:

type CardinalDirection = 'north' | 'south' | 'east' | 'west'

function move(direction: CardinalDirection) {
    // implementation
}

move('north')

Enums

Enumerations provide named constants:

enum Status {
    Pending,
    Active,
    Completed,
    Cancelled
}

function updateStatus(status: Status) {
    console.log('Current status:', status)
}

updateStatus(Status.Active)
updateStatus(Status.Pending)

Numeric Enums with Values:

enum HttpCode {
    OK = 200,
    NotFound = 404,
    ServerError = 500
}

Enums support reverse lookups: HttpCode[200] returns 'OK'.

Unspecified enum values default to numeric indices starting from 0.

Any Type

The any type opts out of type checking entirely:

let flexible: any = 'string'
flexible = 123
flexible.something() // no error, no suggestions

Avoid using any — it eliminates type safety and removes IDE assistance.

Situations that implicitly become any:

  • Variables declared without type and without initial value
  • Function parameters missing type annotations

Type Assertions

Override TypeScript's type inference when you know more than the compiler:

function getElement(): HTMLElement | null {
    return document.getElementById('container')
}

const element = getElement() as HTMLDivElement
element.classList.add('active')

Use assertions when the actual runtime type is more specific than what TypeScript can infer.

Generics

Generics create reusable components that work with multiple types:

function identity<T>(value: T): T {
    return value
}

identity<number>(42)
identity<string>('hello')

Type Inference with Generics:

function wrap<T>(value: T): T {
    return value
}

wrap(999)    // inferred as number
wrap('test') // inferred as string

Generic Constraints:

Require generic types to implement specific properties:

interface HasLength {
    length: number
}

function logLength<T extends HasLength>(item: T): void {
    console.log(item.length)
}

logLength('hello')      // works - strings have length
logLength([1, 2, 3])    // works - arrays have length
logLength({ length: 10 }) // works - object has length property

The constraint extends HasLength ensures the type argument has a length property, enabling type-safe access to that property.

Tags: TypeScript javascript Type System Static Typing web development

Posted on Tue, 16 Jun 2026 16:59:25 +0000 by fiddler80