Skip to content

TypeScript

TypeScript 是 JavaScript 的超集,添加了静态类型系统。所有合法的 JavaScript 代码都是合法的 TypeScript 代码。TS 代码最终编译为普通 JS 运行。

对熟悉 Kotlin/Java/Dart 的开发者来说,TypeScript 的类型系统与它们类似,但更灵活(结构化类型,而非名义类型)。


安装与配置

npm install -D typescript
npx tsc --init     # 生成 tsconfig.json

常用 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020", // 编译目标 JS 版本
    "module": "ESNext", // 模块系统
    "moduleResolution": "bundler", // 配合 Vite/webpack
    "strict": true, // 开启所有严格检查(强烈推荐)
    "noUncheckedIndexedAccess": true, // 数组访问返回 T | undefined
    "exactOptionalPropertyTypes": true, // 严格区分 undefined 和可选属性
    "lib": ["ES2020", "DOM"],
    "jsx": "react-jsx", // React JSX 支持
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] },
    "outDir": "dist",
    "declaration": true // 生成 .d.ts 类型声明文件
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

基础类型

TypeScript 的原始类型对应关系:

TypeScript Kotlin Java Dart
string String String String
number Int / Double int / double int / double
boolean Boolean boolean bool
null null null null
undefined
any Any Object dynamic
unknown Any(需检查) Object(需检查) Object(需检查)
never Nothing Never
void Unit void void
bigint Long long
let name: string = "Alice";
let age: number = 25;
let isDone: boolean = false;
let nothing: null = null;
let notDefined: undefined = undefined;

// 数组(两种写法等价)
let nums: number[] = [1, 2, 3];
let strs: Array<string> = ["a", "b"];

// 元组(固定长度和类型,类似 Kotlin 的 Pair/Triple)
let point: [number, number] = [10, 20];
let entry: [string, number] = ["age", 25];

// 元组可命名
let rgb: [red: number, green: number, blue: number] = [255, 0, 128];

类型推断

TypeScript 能从赋值自动推断类型,无需总是显式标注(类似 Kotlin 的类型推断):

let message = "hello"; // 推断为 string
let count = 42; // 推断为 number
let flag = true; // 推断为 boolean
let items = [1, 2, 3]; // 推断为 number[]
let mixed = [1, "two", true]; // 推断为 (number | string | boolean)[]

// 函数返回值也会推断
function double(x: number) {
  return x * 2; // 推断返回 number
}

函数

// 参数和返回值类型
function add(a: number, b: number): number {
  return a + b;
}

// 可选参数(? 等同于 Kotlin 的默认值参数,但只能为 undefined)
function greet(name: string, greeting?: string): string {
  return `${greeting ?? "Hello"}, ${name}!`;
}

// 默认参数(同 Kotlin)
function createUser(name: string, role: string = "user") {
  return { name, role };
}

// 剩余参数(类似 Kotlin vararg)
function sum(...nums: number[]): number {
  return nums.reduce((a, b) => a + b, 0);
}

// 函数类型
type Transformer = (input: string) => string;
const toUpper: Transformer = (s) => s.toUpperCase();

// 函数重载(类似 Java/Kotlin 的方法重载)
function format(value: string): string;
function format(value: number): string;
function format(value: string | number): string {
  return typeof value === "string" ? value.trim() : value.toFixed(2);
}

接口(Interface)

接口描述对象的结构,类似 Kotlin 的 interface + data class,或 Dart 的抽象类:

interface User {
  id: number;
  name: string;
  email?: string; // 可选属性(类似 Kotlin 可空类型 String?)
  readonly createdAt: Date; // 只读属性(类似 Kotlin val)
}

// 使用接口
const user: User = {
  id: 1,
  name: "Alice",
  createdAt: new Date(),
};

// 接口可以描述函数
interface Comparator {
  (a: number, b: number): number;
}

// 接口可以描述索引签名(类似 Map<String, Any>)
interface Dictionary {
  [key: string]: string;
}

接口继承

interface Animal {
  name: string;
  sound(): string;
}

interface Pet extends Animal {
  owner: string;
}

interface ServiceDog extends Pet {
  certificationId: string;
}

// 多继承(Java 接口/Kotlin 接口都支持)
interface Flyable {
  fly(): void;
}
interface Swimmable {
  swim(): void;
}
interface Duck extends Animal, Flyable, Swimmable {}

类型别名(Type Alias)

typeinterface 更灵活,可以表达联合类型、交叉类型等:

type ID = string | number; // 联合类型
type Point = { x: number; y: number };
type Callback = () => void;
type AsyncResult<T> = Promise<T | null>;

// interface 和 type 的主要区别:
// - interface 可以被重复声明并自动合并(声明合并)
// - type 不能重复声明
// - type 可以表达联合/交叉/条件类型,interface 不行
// - 扩展类时,类只能 implements interface 或 type(对象形状)

联合类型与交叉类型

联合类型(|

类似 Kotlin 的 sealed class,表示"或"关系:

type StringOrNumber = string | number;
type Status = "loading" | "success" | "error"; // 字面量联合类型
type ID = string | number;

function printId(id: ID) {
  if (typeof id === "string") {
    console.log(id.toUpperCase()); // 这里 TS 知道 id 是 string
  } else {
    console.log(id.toFixed(0)); // 这里 TS 知道 id 是 number
  }
}

交叉类型(&

类似 Kotlin 的接口实现,将多个类型合并为一个,表示"且"关系:

type Serializable = { serialize(): string };
type Loggable = { log(): void };
type Service = Serializable & Loggable; // 必须同时具备两者

// 常用于混入(Mixin)模式
type WithTimestamp<T> = T & { createdAt: Date; updatedAt: Date };
type UserWithTimestamp = WithTimestamp<User>;

字面量类型

字面量可以作为类型,比枚举更轻量:

type Direction = "up" | "down" | "left" | "right"
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type BooleanLike = true | false | 0 | 1

function move(direction: Direction, steps: number) { ... }
move("up", 3)    // ✓
move("UP", 3)    // ✗ 类型错误

// 模板字面量类型(TS 4.1+)
type EventName = `on${Capitalize<string>}`  // onCLick, onChange...
type CssValue = `${number}px` | `${number}rem` | `${number}%`
type Getter<T extends string> = `get${Capitalize<T>}`
type Setter<T extends string> = `set${Capitalize<T>}`

枚举(Enum)

// 数字枚举(类似 Java/Kotlin enum,默认从 0 开始)
enum Direction {
  Up, // 0
  Down, // 1
  Left, // 2
  Right, // 3
}

// 字符串枚举(推荐,调试友好)
enum Status {
  Loading = "LOADING",
  Success = "SUCCESS",
  Error = "ERROR",
}

console.log(Status.Loading); // "LOADING"
console.log(Direction.Up); // 0

// const enum:编译时内联,不生成 JS 对象(性能更好)
const enum Color {
  Red,
  Green,
  Blue,
}
const c = Color.Red; // 编译为 const c = 0

很多 TS 项目更倾向用字面量联合类型替代枚举,因为枚举会生成额外的 JS 运行时代码,而字面量类型是纯类型检查,零运行时开销。


TypeScript 的类与 Kotlin/Java 非常相似:

class Animal {
  // 属性声明(Kotlin 中在构造函数参数里)
  private name: string;
  protected age: number;
  readonly species: string;

  // 构造函数
  constructor(name: string, age: number, species: string) {
    this.name = name;
    this.age = age;
    this.species = species;
  }

  // 简写:参数直接声明为属性(类似 Kotlin 主构造函数)
  // constructor(private name: string, protected age: number) {}

  speak(): string {
    return `${this.name} makes a sound`;
  }

  // getter / setter(类似 Kotlin 属性访问器)
  get displayName(): string {
    return this.name.toUpperCase();
  }

  set displayName(value: string) {
    this.name = value.trim();
  }

  // 静态方法
  static create(name: string): Animal {
    return new Animal(name, 0, "unknown");
  }
}

class Dog extends Animal {
  constructor(
    name: number,
    private breed: string,
  ) {
    super(name, age, "Canis lupus"); // 必须先调用 super
  }

  // 方法重写
  override speak(): string {
    return `${super.speak()}: Woof!`;
  }
}

参数属性简写

// 普通写法
class Point {
  public x: number;
  public y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

// 简写(类似 Kotlin 主构造函数 val/var)
class Point {
  constructor(
    public x: number,
    public y: number,
  ) {}
}

抽象类

abstract class Shape {
  abstract area(): number; // 子类必须实现(类似 Kotlin abstract fun)
  abstract perimeter(): number;

  describe(): string {
    return `面积:${this.area()}, 周长:${this.perimeter()}`;
  }
}

class Circle extends Shape {
  constructor(private radius: number) {
    super();
  }

  area(): number {
    return Math.PI * this.radius ** 2;
  }
  perimeter(): number {
    return 2 * Math.PI * this.radius;
  }
}

实现接口

interface Printable {
  print(): void;
}

interface Serializable {
  serialize(): string;
}

// 类可以实现多个接口(同 Java/Kotlin)
class Document implements Printable, Serializable {
  constructor(private content: string) {}

  print(): void {
    console.log(this.content);
  }
  serialize(): string {
    return JSON.stringify({ content: this.content });
  }
}

泛型

TypeScript 泛型与 Kotlin/Java/Dart 泛型概念基本一致:

// 泛型函数(类似 Kotlin <T> fun)
function identity<T>(value: T): T {
  return value;
}

function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

// 多类型参数
function zip<A, B>(a: A[], b: B[]): [A, B][] {
  return a.map((item, i) => [item, b[i]]);
}

// 泛型接口
interface Repository<T, ID = number> {
  findById(id: ID): Promise<T | null>;
  findAll(): Promise<T[]>;
  save(entity: T): Promise<T>;
  delete(id: ID): Promise<void>;
}

// 泛型类
class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }
  pop(): T | undefined {
    return this.items.pop();
  }
  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }
  get size(): number {
    return this.items.length;
  }
}

const stack = new Stack<number>();
stack.push(1);
stack.push(2);

泛型约束(extends

类似 Kotlin 的泛型上界 <T : Comparable<T>>

// 约束 T 必须有 length 属性
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b;
}

longest("hello", "hi"); // ✓ string 有 length
longest([1, 2], [3]); // ✓ array 有 length
longest(10, 20); // ✗ number 没有 length

// 约束键名必须是对象的属性(类似 Kotlin 的 reified + 反射)
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Alice", age: 25 };
getProperty(user, "name"); // ✓ 返回类型为 string
getProperty(user, "age"); // ✓ 返回类型为 number
getProperty(user, "email"); // ✗ 不存在的属性

默认类型参数

// 类似 Kotlin 的默认参数,但用于泛型
interface ApiResponse<T = unknown> {
  data: T;
  status: number;
  message: string;
}

type UserResponse = ApiResponse<User>; // 显式指定
type RawResponse = ApiResponse; // 使用默认 unknown

类型守卫(Type Guards)

类型守卫用于在运行时缩窄类型范围,类似 Kotlin 的 is + 智能转换:

typeof

function process(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase(); // TS 知道这里是 string
  }
  return value.toFixed(2); // TS 知道这里是 number
}

instanceof

class Cat {
  meow() {}
}
class Dog {
  bark() {}
}

function makeSound(animal: Cat | Dog) {
  if (animal instanceof Cat) {
    animal.meow(); // 智能转换为 Cat
  } else {
    animal.bark(); // 智能转换为 Dog
  }
}

in 操作符

interface Fish {
  swim(): void;
}
interface Bird {
  fly(): void;
}

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    animal.swim(); // 类型缩窄为 Fish
  } else {
    animal.fly(); // 类型缩窄为 Bird
  }
}

自定义类型守卫(is

类似 Kotlin 的 is 检查,但需要手动编写断言函数:

interface Dog {
  bark(): void;
  breed: string;
}
interface Cat {
  meow(): void;
  indoor: boolean;
}

// 返回类型 "value is Dog" 即类型谓词
function isDog(animal: Dog | Cat): animal is Dog {
  return "bark" in animal;
}

function handlePet(pet: Dog | Cat) {
  if (isDog(pet)) {
    console.log(pet.breed); // 已知是 Dog
  } else {
    console.log(pet.indoor); // 已知是 Cat
  }
}

可辨识联合(Discriminated Union)

类似 Kotlin 的 sealed class,通过公共字段区分联合成员:

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rectangle":
      return shape.width * shape.height;
    case "triangle":
      return (shape.base * shape.height) / 2;
    // TS 会检查是否覆盖了所有 case(穷举检查)
  }
}

穷举检查——利用 never 确保没有漏掉 case:

function assertNever(value: never): never {
  throw new Error(`未处理的值:${JSON.stringify(value)}`);
}

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rectangle":
      return shape.width * shape.height;
    default:
      return assertNever(shape); // 如果忘写 triangle,编译报错
  }
}

工具类型

TypeScript 内置了大量工具类型,类似 Kotlin 的扩展函数/标准库:

Partial / Required / Readonly

interface User {
  id: number;
  name: string;
  email: string;
}

// Partial<T>:所有属性变可选(类似 Kotlin copy() 的参数)
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string }

// Required<T>:所有属性变必填(去掉所有 ?)
type RequiredUser = Required<PartialUser>;

// Readonly<T>:所有属性变只读(类似 Kotlin data class)
type FrozenUser = Readonly<User>;
const u: FrozenUser = { id: 1, name: "Alice", email: "a@b.com" };
u.name = "Bob"; // ✗ 编译错误

Pick / Omit

// Pick<T, K>:只保留指定属性(类似 Kotlin 的 select/filter)
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string }

// Omit<T, K>:排除指定属性
type UserWithoutId = Omit<User, "id">;
// { name: string; email: string }

Record

// Record<K, V>:构造键为 K、值为 V 的对象类型(类似 Map<K, V>)
type RolePermissions = Record<string, string[]>;
type CountByStatus = Record<"active" | "inactive" | "pending", number>;

const counts: CountByStatus = {
  active: 5,
  inactive: 2,
  pending: 1,
};

Extract / Exclude

type T1 = "a" | "b" | "c";
type T2 = "b" | "c" | "d";

// Extract:取交集
type Common = Extract<T1, T2>; // "b" | "c"

// Exclude:取差集(从 T1 中排除 T2 中有的)
type OnlyInT1 = Exclude<T1, T2>; // "a"

// 常用场景:从联合类型中剔除 null/undefined
type NonNullString = Exclude<string | null | undefined, null | undefined>;
// 等价于 NonNullable<string | null | undefined>

NonNullable

type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // string

ReturnType / Parameters

function fetchUser(id: number, token: string): Promise<User> { ... }

type FetchReturn = ReturnType<typeof fetchUser>
// Promise<User>

type FetchParams = Parameters<typeof fetchUser>
// [id: number, token: string]

// 常用于推断第三方库函数的类型
type RouterConfig = Parameters<typeof createBrowserRouter>[0]

Awaited

type Result = Awaited<Promise<User>>; // User
type Nested = Awaited<Promise<Promise<number>>>; // number

条件类型

// 类似三元运算符,但在类型层面
type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false

// infer:在条件类型中推断类型变量
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type Value = UnwrapPromise<Promise<string>>; // string
type Same = UnwrapPromise<number>; // number

type ElementType<T> = T extends (infer U)[] ? U : never;
type Item = ElementType<string[]>; // string

映射类型

// 遍历对象属性,逐一转换
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

type Optional<T> = {
  [K in keyof T]?: T[K];
};

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface Person {
  name: string;
  age: number;
}
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }

可空性处理

TypeScript 的可空处理与 Kotlin 的可空类型(?)非常相似:

// 开启 strictNullChecks 后,null 和 undefined 不能赋给其他类型
let name: string = null; // ✗ 错误
let name: string | null = null; // ✓

// 可选链(?. )—— 等同于 Kotlin 的 ?.
const city = user?.address?.city; // user 或 address 为 null/undefined 时返回 undefined

// 空值合并(?? )—— 等同于 Kotlin 的 ?:(Elvis 运算符)
const displayName = user?.name ?? "匿名"; // 为 null/undefined 时用右值

// 非空断言(!)—— 等同于 Kotlin 的 !!
const el = document.getElementById("app")!; // 告诉 TS 此值不为 null

// 可选参数 vs 可空参数
function f(x?: string) {} // x 类型为 string | undefined
function g(x: string | null) {} // x 必须传,但可以是 null

// 空值合并赋值 ??=
let cache: string | null = null;
cache ??= computeValue(); // 仅在 cache 为 null/undefined 时赋值

类型断言

类型断言告诉编译器"我比你更了解这个值的类型",类似 Kotlin/Java 的强制类型转换,但不做运行时检查

// as 语法(推荐)
const input = document.getElementById("name") as HTMLInputElement
input.value = "hello"

// 尖括号语法(JSX 中不可用)
const input = <HTMLInputElement>document.getElementById("name")

// 双重断言(绕过类型检查,尽量避免)
const value = someValue as unknown as string

// satisfies 操作符(TS 4.9+,兼顾推断精度和类型检查)
const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
} satisfies Record<string, string | number[]>

palette.red.forEach(...)   // TS 知道 red 是 number[],不是 string
palette.green.toUpperCase() // TS 知道 green 是 string

声明文件(.d.ts

为没有类型的 JS 库编写类型声明:

// global.d.ts
declare global {
  interface Window {
    analytics: AnalyticsInstance;
  }
}

// 为模块补充类型
declare module "*.svg" {
  const content: string;
  export default content;
}

declare module "*.png" {
  const src: string;
  export default src;
}

// 为 JS 库编写声明(通常由 @types/xxx 包提供)
declare module "some-js-lib" {
  export function doSomething(value: string): number;
  export class SomeClass {
    constructor(options: SomeOptions);
    method(): void;
  }
}

安装社区维护的类型声明:

npm install -D @types/node
npm install -D @types/lodash

命名空间 vs 模块

现代 TypeScript 推荐使用 ES 模块import/export),命名空间(namespace)主要用于类型声明文件中避免命名冲突:

// ✓ 现代写法:ES 模块
// utils/math.ts
export function add(a: number, b: number) {
  return a + b;
}

// main.ts
import { add } from "./utils/math";

// 命名空间(主要在 .d.ts 中使用)
namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }
}

装饰器(Decorators)

装饰器是实验性特性(需 "experimentalDecorators": true),类似 Java 注解 / Kotlin 注解:

// 类装饰器
function Singleton<T extends { new (...args: any[]): {} }>(constructor: T) {
  let instance: T;
  return class extends constructor {
    constructor(...args: any[]) {
      if (!instance) {
        instance = new constructor(...args) as T;
        super(...args);
      }
      return instance;
    }
  };
}

// 方法装饰器
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`调用 ${key},参数:`, args);
    const result = original.apply(this, args);
    console.log(`${key} 返回:`, result);
    return result;
  };
  return descriptor;
}

class UserService {
  @Log
  findUser(id: number) {
    return { id, name: "Alice" };
  }
}

常用模式

类型安全的事件系统

type EventMap = {
  login: { userId: string; timestamp: Date };
  logout: { userId: string };
  error: { message: string; code: number };
};

class EventEmitter<T extends Record<string, unknown>> {
  private handlers = new Map<keyof T, Set<(data: any) => void>>();

  on<K extends keyof T>(event: K, handler: (data: T[K]) => void) {
    if (!this.handlers.has(event)) {
      this.handlers.set(event, new Set());
    }
    this.handlers.get(event)!.add(handler);
  }

  emit<K extends keyof T>(event: K, data: T[K]) {
    this.handlers.get(event)?.forEach((h) => h(data));
  }
}

const emitter = new EventEmitter<EventMap>();
emitter.on("login", ({ userId }) => console.log(userId)); // 类型安全
emitter.emit("login", { userId: "1", timestamp: new Date() });

Builder 模式

class QueryBuilder<T> {
  private filters: Partial<T> = {};
  private limitValue?: number;
  private offsetValue?: number;

  where(filter: Partial<T>): this {
    this.filters = { ...this.filters, ...filter };
    return this;
  }

  limit(n: number): this {
    this.limitValue = n;
    return this;
  }

  offset(n: number): this {
    this.offsetValue = n;
    return this;
  }

  build() {
    return {
      filters: this.filters,
      limit: this.limitValue,
      offset: this.offsetValue,
    };
  }
}

const query = new QueryBuilder<User>()
  .where({ role: "admin" })
  .limit(10)
  .offset(20)
  .build();

类型安全的本地存储

class TypedStorage<T extends Record<string, unknown>> {
  get<K extends keyof T>(key: K): T[K] | null {
    const raw = localStorage.getItem(String(key));
    return raw ? JSON.parse(raw) : null;
  }

  set<K extends keyof T>(key: K, value: T[K]): void {
    localStorage.setItem(String(key), JSON.stringify(value));
  }

  remove<K extends keyof T>(key: K): void {
    localStorage.removeItem(String(key));
  }
}

type AppStorage = {
  token: string;
  userId: number;
  preferences: { theme: "light" | "dark"; language: string };
};

const storage = new TypedStorage<AppStorage>();
storage.set("token", "abc123"); // ✓
storage.set("userId", 42); // ✓
storage.set("userId", "hello"); // ✗ 类型错误
storage.get("preferences")?.theme; // 类型为 "light" | "dark" | undefined

与 Kotlin/Java/Dart 对比速查

概念 TypeScript Kotlin Java Dart
可空类型 string \| null String? @Nullable String String?
可选链 a?.b a?.b a?.b
Elvis/空合并 a ?? b a ?: b a ?? b
非空断言 a! a!! a!
类型判断 typeof / instanceof is instanceof is
智能转换 类型守卫后自动 is 后自动 需手动转换 is 后自动
密封类 可辨识联合 sealed class sealed class sealed class
数据类 interface + Readonly data class Record (Java 16)
扩展函数 无(用模块函数代替) 扩展函数 扩展方法
泛型约束 <T extends U> <T : U> <T extends U> <T extends U>
协变/逆变 结构化类型,自动 out T / in T ? extends T
枚举 enum / 字面量联合 enum class enum enum
解构 const {a, b} = obj val (a, b) = pair final (a, b) = ...
扩展运算符 ...arr *arr ...arr