TypeScript 开发指南

写给 Java 开发者的 TypeScript 完整迁移手册 —— 从类型系统到异步编程,从泛型到工程实践(基于 TS 7.0 与 Java 21/25)

TypeScript 7.0(Go 原生编译器) Java 21 LTS / 25+ ↔ TypeScript 17 个核心主题 60+ 代码对比

🎯01 · 设计哲学对比

TypeScript(TS)是微软开发的 JavaScript 超集,为 JS 增加静态类型系统。Java 开发者天然带着"类型安全"的思维,这在 TS 中是优势;但两者在类型系统的运作方式上有根本差异——Java 是名义类型,TS 是结构化类型

维度 Java TypeScript
语言谱系C++ 系,OOP 先行者(1995)JavaScript 超集(2012)
类型系统名义类型,编译期 + 运行期结构化类型,编译期强检查
编译器javac(C/C++ 实现)tsc(TS 7 起用 Go 重写)
运行时JVM(字节码,JIT/AOT)JS 引擎(V8 / Node.js / 浏览器)
编译产物JAR / class / native image纯 JavaScript(.js)
类型擦除泛型运行期擦除类型编译期完全擦除
空值null + Optionalnull + undefined
异步Virtual Thread / CompletableFuture单线程事件循环 + Promise + async/await
并发多线程 + 锁 / Virtual Thread(21+)单线程 + Worker(消息传递)
数据建模Record(16+)/ Sealed(17+)interface / type 别名 / 联合类型
包管理Maven / Gradlenpm / pnpm / yarn
核心目标"一次编写,到处运行"给 JS 加类型,驾驭大型前端工程
💡 最大的思维转变

Java 开发者学 TS 面临三重转变:① 从名义类型到结构化类型(鸭子类型,无需 implements)——Java 21 的 Record 让"数据类"更接近 TS 的 interface/type,但名义化的本质没变;② 从多线程到单线程事件循环 + async/await——注意 Java 21 的 Virtual Thread 让并发更轻量,但仍是多线程模型;TS 的事件循环没有锁、没有共享内存竞争;③ 类型只在编译期存在,运行期全部擦除(Java 的反射、运行时类型查询在 TS 中完全不存在)。

TypeScript 与 JavaScript 的关系

TS 是 JS 的严格超集——任何合法的 JS 都是合法的 TS。TS 在编译期做类型检查,然后擦除全部类型,生成纯 JS 运行。这意味着:类型是"免费的安全网",但运行时没有类型概念。理解这一点很重要:

⚡ TypeScript 7.0:Go 原生编译器

TS 7.0(2026-06)将编译器用 Go 重写,全量构建速度提升 8~12 倍,利用共享内存多线程。就像 Java 生态的 javac 有 JIT 优化一样,TS 现在也有一流的编译性能。同时 7.0 做了大量"收紧默认值"的破坏性变更:strict 默认开、es5/moduleResolution: node/baseUrl 等旧选项被移除。这意味着从 Java 转来,你默认就处于最严格、最安全的类型检查环境。

⚙️02 · 开发环境

维度JavaTypeScript
IDEIntelliJ IDEA / VS CodeVS Code(最佳体验)/ Cursor
编译器javac(JIT/AOT 优化)tsc 7(Go 原生,8~12x 加速
构建工具Maven / Gradletsc / Vite / Bun / esbuild / swc
包管理Maven Centralpnpm(推荐)/ npm / yarn / Bun
运行时JVM(HotSpot / GraalVM)Node.js 20+ / Deno / Bun / 浏览器
依赖文件pom.xml / build.gradle.ktspackage.json + lockfile
类型声明—(class 文件含类型)@types/*(d.ts 文件)或库自带
测试JUnit 5 / MockitoVitest(推荐)/ Jest / Playwright
静态检查SpotBugs / CheckstyleESLint / Biome / tsc --noEmit

项目结构

📂 TypeScript 项目结构
my-app/
├── package.json               // 依赖与脚本
├── tsconfig.json              // TypeScript 编译配置
├── src/                       // 源码
│   ├── index.ts               // 入口
│   ├── models/                // 数据模型 / 类型
│   ├── services/              // 业务逻辑
│   ├── utils/                 // 工具函数
│   └── types/                 // 全局类型声明(.d.ts)
├── tests/                     // 测试
├── node_modules/              // 依赖(不提交)
└── dist/                      // 编译输出

安装与初始化(TS 7.0)

⌨️ 命令行 · TS 7.0
# 安装 TypeScript 7(Go 原生编译器)
npm install -D typescript@7

# 初始化项目
npm init -y
tsc --init                 # 生成 tsconfig.json(已内置 strict:true)

# 编译(7.0 默认多线程并行,默认用 4 个 checker)
tsc                        # 读 tsconfig.json 编译,8~12x 加速
tsc --watch                # 监听模式(基于 Parcel watcher,更稳定)

# 并行调优(可选)
tsc --checkers 8           # 提高类型检查并行度(默认4,加快构建)
tsc --singleThreaded       # 完全禁用并行(调试/CI 低内存)
✅ TS 7 编译快到你感受不到

TS 7.0 用 Go 重写编译器,vscode 代码库全量构建从 125 秒降到 10.6 秒,sentry 从 140 秒降到 16 秒——比 Java 的 javac 增量编译快一个量级。对 Java 开发者是惊喜:不用再等编译。同时默认 strict: true,等于 Java 里"所有告警全开并视为错误"的体验,开箱即得。

✅ VS Code 对 Java 开发者友好

VS Code 对 TypeScript 有一等公民支持——开箱即用类型推断、自动补全、跳转定义、重构。Java 开发者已熟悉 IntelliJ 快捷键的话,VS Code 学习成本很低,而且 TS 项目比 Java 项目启动快得多、无 JVM 启动开销。

👋03 · Hello World

先看一个最简单的对比,感受 TS 的"类型注解 + 类型推断 + 类型擦除"。

☕ Java 21+
public class Hello {
    public static void main(String[] args) {
        var name = "World";   // Java 10+ var 推断
        System.out.println("Hello, " + name);
    }
}
✨ TypeScript 7.0
function greet(name: string): void {
  console.log(`Hello, ${name}`)
}

let name = 'World'   // 类型推断为 string
greet(name)

关键差异

💡 编译产物是纯 JS

上面的 TS 代码编译后,所有类型注解被擦除,变成:function greet(name) { console.log('Hello, ' + name) }。类型只在开发时提供安全保障,运行时没有任何类型检查开销——这比 Java 的擦除更彻底,连泛型在运行期都完全不存在。

📦04 · 类型系统

这是 Java 与 TS 差异最大的地方。TS 是结构化类型(Structural Typing)——只要对象结构匹配就能赋值;Java 是名义类型——两个类即使结构完全相同也不能互相赋值(除非有继承/实现关系)。

基本类型对比

概念JavaTypeScript
整数byte/short/int/longnumber(统一,64 位浮点)
浮点float/doublenumber
大整数BigInteger(引用)bigint(原生字面量 123n
布尔booleanboolean
字符串String(引用)string(值,不可变)
空值nullnull + undefined
任意类型Objectany / unknown(安全)
联合类型无(只能用继承层次)string | number
字面量类型'red' | 'blue'
元组[string, number]
类型推断var(10+)/ record(16+)默认推断,无需关键字

变量声明

☕ Java 21+
var name = "Alice";          // Java 10+ var 推断
final int age = 18;
var list = new ArrayList<String>();

// Java 16+ record:数据载体(类似 TS 的 interface)
public record Point(int x, int y) {}

String s = null;
✨ TypeScript 7.0
let name: string = 'Alice'
const age: number = 18
let list: string[] = []

// 对应 TS:interface 作数据载体(结构化、无实现)
interface Point { x: number; y: number }

let s: string | null = null
let u: string | undefined = undefined

// 类型推断(无需写类型)
let x = 42      // 推断为 number
let y = 'hi'    // 推断为 string

// 联合类型(Java 没有)
let id: string | number = 123
id = 'abc'      // ✅ 合法
⚠️ let vs const vs var

let:可变变量(类似 Java 局部变量);const:不可变绑定(类似 Java final);var不要使用(函数作用域,有提升问题)。TS 中一律用 letconst

结构化类型(鸭子类型)

✨ TypeScript · 结构化类型
interface Person {
  name: string
  age: number
}

// 不需要 implements Person!只要结构匹配就能赋值
let p: Person = {
  name: 'Alice',
  age: 18
}

// class 也能赋值给 interface(结构匹配即可)
class User {
  name: string = ''
  age: number = 0
}
let u: Person = new User()  // ✅ 结构匹配

// 直接传对象字面量
function printPerson(person: Person): void {
  console.log(person.name)
}
printPerson({ name: 'Bob', age: 20 })  // ✅ 合法

any vs unknown

✨ TypeScript · any 与 unknown
// any:放弃类型检查(尽量不用)
let a: any = 1
a.anything()        // 不报错,危险

// unknown:安全的"未知类型",需收窄后才能用
let u: unknown = 1
// u.toFixed()       // ❌ 报错,unknown 不能直接调用方法

// 收窄后才能用
if (typeof u === 'number') {
  u.toFixed()   // ✅ 收窄为 number
}

类型断言 vs 强制转换

☕ Java · 强制转换
Object obj = "hello";
String s = (String) obj;  // 运行时 ClassCastException 风险

if (obj instanceof String) {
    String s2 = (String) obj;
}
✨ TypeScript · 类型断言
let obj: unknown = 'hello'
let s = obj as string   // 编译期断言,无运行时检查

// 安全收窄:typeof / instanceof
if (typeof obj === 'string') {
  let s2 = obj   // 自动收窄为 string
}

// 注意:断言只影响编译期,运行时仍是别的值
let n = 'not a number' as number  // 编译通过,但运行时是字符串!

satisfies 运算符 —— 类型安全的字面量

TS 4.9 引入 satisfies,是断言的现代安全替代——既保留字面量的精确类型,又强制满足目标类型。Java 没有对应物。

✨ TypeScript · satisfies
// ❌ as 断言:丢失字面量精度,且不验证
const config1 = { port: 8080 } as Config
// config1.port 类型是 number,不是 8080

// ✅ satisfies:保留字面量精度,且强制满足 Config
const config2 = { port: 8080 } satisfies Config
// config2.port 类型是 8080(字面量),同时保证符合 Config 结构

// 编译期就能发现结构错误
const bad = { port: '8080' } satisfies Config  // ❌ 编译错误

null 与 undefined 的区别

Java 只有 null;TS 有 null(显式空值)和 undefined(未初始化/未定义)。null == undefined 为 true,但 null === undefined 为 false。严格模式(strictNullChecks)下,两者都不能赋值给非空类型。

✨ TypeScript · 空值处理
// 严格模式下不能给非空类型赋 null/undefined
let name: string = null   // ❌ 编译错误(strictNullChecks)

// 显式标注可空
let name2: string | null = null

// 空值合并运算符(类似 Optional.orElse)
let display = name2 ?? 'unknown'

// 可选链(类似 Optional.map)
let len = name2?.length   // name2 为 null 时返回 undefined,不报错

// 非空断言(告诉编译器"我确定不为 null")
let len2 = name2!.length   // 有风险,尽量用可选链替代

🔀05 · 控制流

TS 的控制流与 Java 基本一致(if/elseforwhileswitch),但有两个关键差异:严格相等===)和类型收窄(narrowing)。

条件语句

☕ Java
if (x > 0) { ... }
else if (x == 0) { ... }
else { ... }

// 条件必须是 boolean
if (obj != null) { ... }

// 三元运算符
String s = x > 0 ? "pos" : "neg";
✨ TypeScript
if (x > 0) { ... }
else if (x === 0) { ... }
else { ... }

// 类型守卫:if 内自动收窄类型
if (obj != null) {
  obj.toString()  // 自动推断为非 null
}

// 三元运算符
let s = x > 0 ? 'pos' : 'neg'
⚠️ === vs ==

Java 用 == 比较基本类型、.equals() 比较对象;TS 用 ===(严格相等,不转换类型)和 ==(宽松相等,会转换类型)。TS 中推荐始终用 ===,避免隐式类型转换导致的 bug。例如 '1' == 1 为 true,但 '1' === 1 为 false。

for 循环

☕ Java
// 经典 for
for (int i = 0; i < 10; i++) { ... }

// 增强 for
for (String s : list) { ... }

// while
while (cond) { ... }
✨ TypeScript
// 经典 for
for (let i = 0; i < 10; i++) { ... }

// for...of(遍历值)
for (const s of list) { ... }

// for...in(遍历键)
for (const key in obj) { ... }

// while
while (cond) { ... }

// forEach
list.forEach((item, index) => {
  console.log(`${index}: ${item}`)
})

switch

☕ Java 21+ · switch expression
// Java 14+ switch expression(无穿透,更安全)
var kind = switch (day) {
    case "SAT", "SUN" -> "weekend";
    default -> "weekday";
};

// Java 21+ pattern matching(switch 类型匹配)
static String format(Object o) {
    return switch (o) {
        case Integer i -> "int: " + i;
        case String s -> "str: " + s;
        case null -> "null";
        default -> "other";
    };
}
✨ TypeScript · 需 break
// TS 仍是传统 switch(需 break),无 switch expression
switch (day) {
  case 'SAT':
  case 'SUN':       // 穿透
    weekend()
    break
  default:
    weekday()
}

// 类型匹配用可辨识联合 + 字面量收窄
type Shape =
  | { kind: 'circle'; r: number }
  | { kind: 'square'; s: number }

function area(x: Shape): number {
  switch (x.kind) {
    case 'circle':
      return Math.PI * x.r ** 2  // ✅ 收窄为 circle
    case 'square':
      return x.s ** 2        // ✅ 收窄为 square
  }
}
💡 Java 21+ 的 pattern matching 接近 TS 的可辨识联合

Java 21 的 switch 模式匹配(JEP 441)让 case Integer i -> 这样的语法成为可能,TS 早就用可辨识联合 + 字面量字段达到了类似效果。但 TS 没有 switch expression——需要靠穷尽性检查(never)保证分支完整。

模板字符串 —— Java 没有的利器

✨ TypeScript
let name = 'Alice'
let age = 18

// 模板字符串(反引号)
let msg = `Name: ${name}, Age: ${age}`

// 多行字符串
let html = `<div>
  <p>${name}</p>
</div>`

// 表达式嵌入
let result = `Total: ${items.reduce((a, b) => a + b, 0)}`

🔧06 · 函数

TS 函数比 Java 灵活得多——支持可选参数、默认值、箭头函数、函数类型。Java 需要函数式接口(@FunctionalInterface)才能传递函数,TS 直接传递。

☕ Java
// 普通方法
public int add(int a, int b) {
    return a + b;
}

// 函数式接口 + Lambda
@FunctionalInterface
interface IntFunc {
    int apply(int x);
}

IntFunc square = x -> x * x;

// 无默认参数值,需重载
public void log(String msg) { log(msg, "INFO"); }
public void log(String msg, String level) { ... }
✨ TypeScript
// 普通函数
function add(a: number, b: number): number {
  return a + b
}

// 箭头函数(Lambda)
const square = (x: number): number => x * x

// 函数类型直接写
type IntFunc = (x: number) => number
let f: IntFunc = (x) => x * x

// 默认参数 + 可选参数(Java 需重载)
function log(msg: string, level: string = 'INFO'): void { ... }
function greet(name: string, title?: string): void { ... }

// 剩余参数(Java 的 int...)
function sum(...nums: number[]): number {
  return nums.reduce((a, b) => a + b, 0)
}

箭头函数 vs 普通函数 —— this 绑定

这是 Java 开发者最容易困惑的点。Java 的 Lambda 自动捕获 this;TS 的普通函数有自己的 this(动态绑定),箭头函数继承外层 this

✨ TypeScript · this 绑定差异
class Counter {
  count: number = 0

  // ❌ 普通函数:this 不指向 Counter
  bad() {
    setTimeout(function() {
      this.count++  // this 是 undefined 或全局对象!
    }, 1000)
  }

  // ✅ 箭头函数:this 继承自 Counter
  good() {
    setTimeout(() => {
      this.count++  // this 指向 Counter 实例 ✅
    }, 1000)
  }
}
⚠️ 回调中始终用箭头函数

在事件回调、异步回调、setTimeout 等场景中,始终用箭头函数而非普通 function,否则 this 会丢失。这是从 Java 过来最容易踩的坑——Java 的 Lambda 没有这个问题。

🏗️07 · 面向对象

TS 支持 class、interface、继承、多态、抽象类、泛型,语法与 Java 相似但更灵活。最大的差异是 interface 的结构化语义和 TS 的类字段简化写法

☕ Java
public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() { return name; }
    public void speak() {
        System.out.println(name);
    }
}

class Dog extends Animal {
    public Dog(String name) { super(name); }

    @Override
    public void speak() {
        System.out.println("Woof");
    }
}

Animal a = new Dog("Rex");
a.speak();  // Woof(多态)
✨ TypeScript
class Animal {
  private name: string

  constructor(name: string) {
    this.name = name
  }

  getName(): string { return this.name }
  speak(): void {
    console.log(this.name)
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name)
  }

  // override 关键字(TS 4.3+)
  override speak(): void {
    console.log('Woof')
  }
}

const a: Animal = new Dog('Rex')
a.speak()  // Woof(多态)

构造器参数简写 —— Java 没有的语法糖

✨ TypeScript · 参数属性简写
// Java 写法(冗长)
// class User { private String name; private int age;
//   public User(String name, int age) { this.name = name; this.age = age; } }

// TS 简写:构造器参数前加修饰符,自动声明并赋值字段
class User {
  constructor(public name: string, private age: number) {}
}

const u = new User('Alice', 18)
u.name  // ✅ 自动成为 public 字段
// u.age  // ❌ private,外部不可访问

interface —— 结构化而非名义化

☕ Java · 显式 implements
interface Speaker {
    void speak();
}

class Dog implements Speaker {
    @Override
    public void speak() { ... }
}

// 接口不能有字段,只能有方法
// (Java 8+ 可有 default 方法)
✨ TypeScript · 隐式实现
interface Speaker {
  speak(): void
}

class Dog {
  // 不需要 implements Speaker!
  // 有 speak() 方法就自动满足
  speak(): void { ... }
}

let s: Speaker = new Dog()  // ✅ 结构匹配

// interface 可含字段(数据结构定义)
interface User {
  name: string
  age: number
}

访问修饰符

JavaTypeScript说明
publicpublic(默认)公开
privateprivate类内可见(TS 是编译期约束)
protectedprotected子类可见
包内可见(默认)TS 无包级可见性
readonlyreadonly只读(类似 final 字段)
staticstatic静态成员
abstractabstract抽象类/方法
⚠️ private 只是编译期约束

TS 的 private 只在编译期检查,运行时(编译后)其实不存在——任何 JS 代码都能访问。#privateField(ES 原生私有字段)才是运行时真正私有。绝大多数场景用 TS 的 private 就足够,但要知道它不是 Java 那样的强封装。

Record 与 Sealed Class —— Java 16/17 引入(已纳入 Java 21 LTS)对 TS 类型别名的呼应

Java 16/17 引入 record(不可变数据载体)和 sealed class(封闭继承层次,Java 21 进一步用模式匹配 switch 完善),是对 TS 灵活类型的回应。但本质仍是名义类型,需要在编译期显式声明,与 TS 的结构化语义不同。

☕ Java 21+ · record + sealed
// record:自动生成构造器/equals/hashCode/toString
public record User(String name, int age) {}

// sealed:限制可继承的子类(17+ 正式)
public sealed interface Shape
    permits Circle, Square {}

public record Circle(double r) implements Shape {}
public record Square(double s) implements Shape {}
✨ TypeScript · type 别名 + 联合
// interface:数据载体(等价 record,但更轻)
interface User {
  name: string
  age: number
  readonly id: string   // 显式不可变
}

// type 联合:等价 sealed(封闭成员集合)
type Shape =
  | { kind: 'circle'; r: number }
  | { kind: 'square'; s: number }

// 穷尽性检查:switch 覆盖所有分支,编译期验证
function area(x: Shape): number {
  switch (x.kind) {
    case 'circle': return Math.PI * x.r ** 2
    case 'square': return x.s ** 2
  }
}
✅ Java record 是对 TS interface 的致敬

Java 16 的 record 是对"数据类不该写一堆样板代码"的回应;sealed 是对"封闭继承层次"的回应。TS 早就用 interface + type 联合类型 + 穷尽性检查达到了类似效果,而且更灵活——结构匹配即可,不用 permits 显式声明。从 Java 21+ 过来,你会发现 TS 的联合类型表达力更强。

🗂️08 · 集合与数组

JavaTypeScript说明
ArrayListArray<T> / T[]动态数组
HashMapMap<K, V>键值对
HashSetSet<T>集合
LinkedList用 Array 替代
Collections.sort()arr.sort()排序
Stream APIarr.filter/map/reduce函数式操作
☕ Java · 集合操作
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
String first = list.get(0);

Map<String, Integer> map = new HashMap<>();
map.put("a", 1);

// Stream
List<String> result = list.stream()
    .filter(s -> s.length() > 0)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
✨ TypeScript · 数组操作
let list: string[] = ['a', 'b']
list.push('c')
let first = list[0]

let map = new Map<string, number>()
map.set('a', 1)
map.get('a')  // 1

// 函数式操作(链式调用)
let result = list
  .filter(s => s.length > 0)
  .map(s => s.toUpperCase())

// reduce
let total = [1, 2, 3].reduce((a, b) => a + b, 0)

TS 数组特有操作

✨ TypeScript
let arr: number[] = [3, 1, 2]

// 添加/删除
arr.push(4)           // 尾部添加
arr.pop()              // 尾部删除
arr.unshift(0)         // 头部添加
arr.shift()             // 头部删除
arr.splice(1, 1)        // 索引1删1个

// 查找
arr.includes(2)         // true
arr.indexOf(2)           // 2
arr.find(x => x > 1)       // 3(第一个满足条件的)

// 变换
arr.sort((a, b) => a - b) // 排序(原地)
arr.reverse()            // 反转(原地)
let sliced = arr.slice(0, 2)  // 截取(返回新数组)

// 展开(类似 Java 的 list.addAll)
let combined = [...arr, 5, 6]

// 解构赋值
let [a, b, ...rest] = [1, 2, 3, 4]
// a=1, b=2, rest=[3,4]

🧩09 · 高级类型

这是 TypeScript最独特的能力,Java 完全没有对应物。高级类型让类型本身可以"编程"——做条件判断、映射、抽取。理解它们是从 Java 到 TS 的核心跃迁。

联合类型与交叉类型

☕ Java · 无对应物
// Java 没有联合类型,只能用继承层次表达
interface Shape { ... }
class Circle implements Shape { ... }
class Square implements Shape { ... }

// 取值时需 instanceof 判断
if (shape instanceof Circle) { ... }
✨ TypeScript · 联合/交叉类型
// 联合类型(或):值可以是任一类型
type Shape = Circle | Square

// 交叉类型(与):同时满足所有约束
type Named = { name: string }
type Aged = { age: number }
type Person = Named & Aged
// Person = { name: string; age: number }

// 可辨识联合 + 类型守卫(代替 instanceof)
type Result<T> =
  | { status: 'success'; data: T }
  | { status: 'error'; error: string }

function handle(r: Result<number>) {
  if (r.status === 'success') {
    r.data.toFixed()  // ✅ 自动收窄
  } else {
    r.error          // ✅ 收窄为 error 分支
  }
}

字面量类型与类型别名

✨ TypeScript · 字面量类型
// 字面量类型:值本身作为类型
let direction: 'north' | 'south' | 'east' | 'west' = 'north'
direction = 'up'  // ❌ 编译错误

// 类型别名
type Direction = 'north' | 'south' | 'east' | 'west'

// 枚举替代(Java 的 enum)
enum Status { Active = 'ACTIVE', Inactive = 'INACTIVE' }
const s: Status = Status.Active

映射类型与条件类型

✨ TypeScript · 映射/条件类型
interface User {
  id: number
  name: string
  email: string
}

// 映射类型:批量转换所有属性
type ReadonlyUser = { readonly [K in keyof User]: User[K] }
type NullableUser = { [K in keyof User]: User[K] | null }

// 内置映射类型(高频使用)
type A = Partial<User>    // 所有属性可选
type B = Required<User>   // 所有属性必填
type C = Pick<User, 'id' | 'name'>  // 挑选部分属性
type D = Omit<User, 'email'>   // 排除部分属性
type E = Record<'a' | 'b', number> // { a: number; b: number }

// 条件类型(类型层面的 if/else)
type IsString<T> = T extends string ? 'yes' : 'no'
type R1 = IsString<string>  // 'yes'
type R2 = IsString<number>  // 'no'
✅ 高级类型是 TS 的核心武器

Java 的类型系统是"数据类型的集合";TS 的类型系统是可以编程的。虽然日常开发用 PartialPickOmit 就够,但理解"类型即值"的思维,是区分"会用 TS"和"驾驭 TS"的分水岭。遇到复杂数据结构时,用类型层面解决往往比写业务代码更优雅。

🧬10 · 泛型

TS 泛型语法与 Java 相似,但有一个关键差异:Java 泛型是类型擦除(运行时 List<String>List<Integer> 是同一个类);TS 泛型在编译期完全展开后擦除,类型信息全部编译掉。

☕ Java · 类型擦除泛型
public class Box<T> {
    private T value;
    public Box(T value) { this.value = value; }
    public T get() { return value; }
}

Box<String> box = new Box<>("hi");
String s = box.get();

// 泛型方法
public <T> T first(List<T> list) {
    return list.get(0);
}
✨ TypeScript · 编译期泛型
class Box<T> {
  value: T
  constructor(value: T) {
    this.value = value
  }
  get(): T { return this.value }
}

const box = new Box<string>('hi')
const s = box.get()

// 泛型函数
function first<T>(arr: T[]): T {
  return arr[0]
}

// 泛型约束(extends)
function getLength<T extends { length: number }>(arg: T): number {
  return arg.length
}

TS 泛型的独特能力

✨ TypeScript · keyof 与类型推断
interface Config {
  host: string
  port: number
}

// keyof:获取对象的所有键(Java 需反射)
type ConfigKeys = keyof Config  // 'host' | 'port'

// 泛型 + keyof + 泛型推断:类型安全的属性访问
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const c: Config = { host: 'localhost', port: 8080 }
const h = getProperty(c, 'host')  // h: string ✅
// getProperty(c, 'xxx')  // ❌ 编译错误,'xxx' 不是 Config 的键

// 泛型参数默认值
function wrap<T = string>(v: T): T { return v }
✅ keyof + 泛型 是 TS 的安全魔法

Java 读取对象属性需要反射(Field / getDeclaredField),运行时开销大、不安全;TS 用 keyof + 泛型在编译期就保证键名正确——既安全又零运行时开销。这是"类型即安全"哲学的极致体现。

📚11 · 模块化

☕ Java · import
import java.util.List;
import com.example.User;
import static java.lang.Math.PI;
✨ TypeScript · import/export
// utils/math.ts —— 导出
export function add(a: number, b: number): number {
  return a + b
}
export const PI = 3.14

// index.ts —— 导入
import { add, PI } from './utils/math'

// 默认导出(每个文件最多一个)
export default class User { ... }
import User from './models/User'

// 导入第三方库
import express from 'express'
import { useState } from 'react'
概念JavaTypeScript
导入语法import pkg.Class;import { name } from 'path'
导出public 关键字export / export default
包管理Maven Centralnpm registry
依赖文件pom.xml / build.gradlepackage.json
通配符导入import pkg.*import * as ns from 'path'
类型声明导入import type { Foo }(纯类型)
💡 .d.ts 类型声明文件

JS 库没有类型,TS 社区通过 @types/* 提供类型声明(.d.ts 文件)。Java 的库自带类型(class 文件含类型信息),TS 需要单独装类型包:npm install @types/node。现代库多数直接内置类型(自带 .d.ts),无需额外安装。

12 · 异步编程

这是 TS 与 Java 差异最大、也最重要的概念。Java 用多线程 + CompletableFuture + 回调;TS 运行在单线程事件循环上,用 Promise + async/await——语言级支持,无需管理线程。

💡 单线程事件循环

TS/JS 的异步不是"开新线程",而是单线程上的协作式调度。一次只执行一个任务,遇到 await 就挂起、让出控制权,等异步操作完成后再继续。这消除了 Java 中最头疼的共享内存竞争死锁问题——因为根本没有多线程同时访问内存。

☕ Java 21+ · CompletableFuture / Virtual Thread
// 方式1:CompletableFuture 链(传统)
CompletableFuture.supplyAsync(() -> fetchData())
    .thenApply(parse)
    .thenAccept(System.out::println)
    .exceptionally(e -> { e.printStackTrace(); return null; });

// 方式2:Java 21+ Virtual Thread(更接近同步写法)
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
    var data = exec.submit(() -> fetchData).get();
    var result = parse(data);
    System.out.println(result);
}  // Virtual Thread 在 IO 上轻量,但仍需线程管理
✨ TypeScript · async/await
async function loadData(): Promise<void> {
  try {
    const data = await fetchData()
    const result = parse(data)
    console.log(result)
  } catch (e) {
    console.error(`Error: ${e}`)
  }
}

Promise 基础

✨ TypeScript · Promise 创建与使用
// 创建 Promise(类似 CompletableFuture.supplyAsync)
function fetchUser(id: number): Promise<User> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ name: 'Alice', age: 18 })
      } else {
        reject(new Error('Invalid id'))
      }
    }, 1000)
  })
}

// 方式1:then/catch(类似 CompletableFuture 链)
fetchUser(1)
  .then(user => console.log(user.name))
  .catch(err => console.error(err))

// 方式2:async/await(推荐,更可读)
async function main() {
  try {
    const user = await fetchUser(1)
    console.log(user.name)
  } catch (e) {
    console.error(`Failed: ${e}`)
  }
}

并发 Promise(类似 CompletableFuture.allOf)

✨ TypeScript · Promise 并发
async function loadAll() {
  // 并发发起 3 个请求,全部完成后继续
  const [user, posts, friends] = await Promise.all([
    fetchUser(1),
    fetchPosts(1),
    fetchFriends(1)
  ])
  // 三个结果同时可用
}

// 任一完成即返回(谁先到用谁,类似竞速)
const fastest = await Promise.race([
  fetchFromCache(),
  fetchFromNetwork()
])

// 全部成功才成功,否则 reject(类似 allOf + 整体失败)
const results = await Promise.allSettled([
  task1(), task2(), task3()
])
✅ async/await 是最佳实践

优先用 async/await 而非 .then().catch() 链——代码读起来像同步代码,异常处理用 try/catch,比 CompletableFuture 的链式调用和异常处理更直观。注意:async/await 不是多线程,不解决 CPU 密集任务——真正的并行需要 Worker 线程。

🚫 不要忘记 await

漏写 await 会拿到 Promise 对象而非结果,导致类型错误或逻辑 bug。TS 编译器会提示"类型 Promise<User> 不能赋值给 User"——所以类型系统帮你抓住了漏 await 的错误。这是 Java 里没有的保障。

🏷️13 · 装饰器

TS 装饰器(Decorator)与 Java 注解(Annotation)都是"元编程"手段,但工作方式不同:Java 注解是运行时元数据,由框架通过反射处理;TS 装饰器是函数,在类定义时被调用,可以直接修改类的行为

✅ TS 5.0 标准装饰器(TS 7 沿用并默认开启)

TypeScript 5.0 把ECMAScript 标准装饰器作为正式特性内置——不再需要在 tsconfig 里加 "experimentalDecorators": true。到 TS 7.0 这一语法已是默认且唯一支持的形式,与 NestJS、TypeORM 等老生态用的"实验性装饰器"语法略有差异,迁移时需注意。但语义都比 Java 注解更"活跃":是函数,不是标记。

维度Java 注解TS 装饰器
本质运行时元数据(标记)编译期/运行期函数
处理方式反射 + 框架扫描定义时直接调用
能否改行为不能,需框架配合,直接包装方法/类
性能反射有开销几乎零开销
应用Spring、JPA、LombokNestJS、TypeORM、Angular
☕ Java · 注解
@RestController
public class UserController {

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}
// 注解只是"标记",真正逻辑在 Spring 框架反射里
✨ TypeScript · 装饰器
@Controller('/user')
class UserController {

  @Get('/:id')
  getUser(@Param('id') id: number): User {
    return userService.findById(id)
  }
}
// 装饰器在类定义时执行,直接修改/注册类
💡 装饰器是函数,可以"做事"

Java 注解不能包含逻辑(只是标记);TS 装饰器是函数,在定义时被调用,能修改构造器、方法、属性。例如 @sealed 能冻结类,@logged 能自动给方法加日志。这是 Java 注解做不到的。

🗄️14 · 工程与配置

tsconfig.json —— TS 7.0 新默认值

TS 6/7 大幅收紧默认值,开箱即得严格、现代、安全的配置。下面左边是 7.0 的默认值,右边是你可能仍需显式设置的选项。

✨ TypeScript 7.0 · 现代 tsconfig
{
  "compilerOptions": {
    // ── 以下为 TS 7 默认值(无需显式写)──
    // "strict": true,            // 默认开启(6+)
    // "module": "esnext",        // 默认(7)
    // "target": <当前稳定ES>,     // 默认(7)
    // "esModuleInterop": true,   // 强制(7)
    // "alwaysStrict": true,      // 固定(7)

    // ── 你可能仍需显式设置的 ──
    "moduleResolution": "bundler",   // 或 "nodenext"(Node 20+)
    "noUncheckedIndexedAccess": true,  // arr[0] 类型为 T | undefined
    "exactOptionalPropertyTypes": true, // 区分 ?: 与 | undefined
    "noImplicitOverride": true,       // 强制写 override(对应 Java)
    "verbatimModuleSyntax": true,    // import type 必须显式
    "outDir": "./dist",
    "sourceMap": true
  },
  // TS 7: rootDir 默认 ./,需显式指定源码目录
  "include": ["src"]
}
⚠️ TS 7 移除的旧选项(从 Java 转来别踩)

以下旧配置在 TS 7 中变成硬错误,如果从老项目迁移需先改掉:

  • target: es5downlevelIteration —— 不再支持
  • moduleResolution: node/node10/classicbaseUrl —— 改用 nodenextbundler
  • module: amd/umd/systemjs/none —— 改用 esnext/nodenext
  • esModuleInterop / allowSyntheticDefaultImports —— 不能设为 false
  • alwaysStrict —— 固定为 true
  • 命名空间里用 module 关键字 —— 改用 namespace

依赖管理对比

维度JavaTypeScript
依赖清单pom.xml / build.gradlepackage.json
仓库Maven Centralnpm registry
安装命令mvn installnpm install
版本锁定版本号package-lock.json
运行时依赖compile scopedependencies
开发依赖test scopedevDependencies(tsc、测试等)
✅ strict 现在默认开启(TS 6+)

TS 6/7 起 strict: true默认值,不用再手动配。它一次性开启所有严格检查(noImplicitAnystrictNullChecks 等),在编译期消灭大量运行时 bug。这相当于 Java 里同时开启所有编译告警并当作错误处理——对 Java 开发者来说,这正是你习惯的"类型安全默认强约束"体验,开箱即得。

🛡️15 · 空安全与 Optional

Java 8 用 Optional<T> 处理空值;TS 用联合类型 + 类型收窄 + 空值运算符,更简洁且无装箱开销。

☕ Java · Optional
public Optional<String> findName(Long id) { ... }

// 链式处理
String name = findName(id)
    .map(String::toUpperCase)
    .orElse("unknown");
✨ TypeScript · 联合类型
function findName(id: number): string | null { ... }

// 空值合并(类似 orElse)
const name = findName(id)?.toUpperCase() ?? 'unknown'

// ?. 可选链:中途为 null 立即短路,不报错
const city = user?.address?.city

// ?? 空值合并:null/undefined 时用默认值
const display = user?.name ?? 'Guest'

// 类型收窄:if 判断后自动非空
const n = findName(1)
if (n !== null) {
  n.toUpperCase()  // ✅ 编译器知道 n 非 null
}

对比:Java Optional vs TS 空安全

操作Java OptionalTypeScript
默认值.orElse(x)?? x
映射.map(f)?.f()
过滤.filter(p)if (x !== null)
判空.isPresent()x !== null
空即抛异常.orElseThrow()if (!x) throw
✅ TS 空安全更轻量

Java 的 Optional 是包装对象,有分配开销,且容易误用(把 null 塞进 Optional、对 Optional 再判空)。TS 直接把可空性写进类型string | null),编译器强制你在使用前收窄——空安全是类型系统的一部分,而非事后包装。这是 TS 比 Java 更彻底的空安全设计。

💣16 · Java 开发者常见陷阱

1. this 绑定丢失

Java Lambda 自动捕获 this;TS 普通函数 this 动态绑定。回调用箭头函数

2. === vs ==

始终用 === 严格相等。'1' == 1true(隐式转换),'1' === 1false

3. null 和 undefined 不同

Java 只有 null;TS 有 null(显式空)和 undefined(未初始化)。null == undefined 为 true,但 null === undefined 为 false。

4. number 是浮点

TS 没有独立整数类型,number 底层是 64 位浮点。0.1 + 0.2 !== 0.3——浮点精度问题始终存在。金额计算要考虑用 BigInt 或整数分。

5. 数组是对象,不是连续内存

Java 数组是连续内存块;JS/TS 数组是动态的、稀疏的,性能特征不同。大量数值计算考虑 TypedArray

6. 不支持方法重载

TS 函数不能像 Java 那样同名不同参。用可选参数 title?: string、默认值 level = 'INFO',或联合类型参数替代。

7. 异步不是多线程

async/await 是单线程异步(事件循环),不创建新线程,不解决 CPU 密集任务。真正的并行需要 Worker 线程。

8. 类型断言不是类型转换

as 只在编译期生效,不改变运行时值。'abc' as number 编译通过但运行时仍是字符串。

9. interface 编译后消失

不能对 interface 用 instanceof(interface 没有运行时存在)。用可辨识联合的 status 字段或 typeof 收窄。

10. private 只是编译期约束

TS 的 private 运行时不存在(编译后被擦除),任何 JS 都能访问。真正私有用 #field(ES 原生)。

11. 不要在 async 中丢异常

async 函数抛出的异常会变成 rejected Promise,必须 try/catch.catch(),否则可能静默吞掉(unhandled rejection)。

12. 对象字面量类型推断过窄

const arr = ['a', 'b'] 推断为 string[],但 let x = { name: 'a' }name 推断为 string 而非 'a'。需要字面量类型时用 as const

13. noUncheckedIndexedAccess 让数组访问变可空

开启 noUncheckedIndexedAccess 后,arr[0] 的类型是 T | undefined——因为索引可能越界。这是更安全的选择,但会让代码变啰嗦。Java 数组越界抛 ArrayIndexOutOfBoundsException,TS 让你显式处理。

14. const enum 在 isolatedModules 下不可用

const enum 在跨文件单文件编译(esbuild/swc)时无法内联,现代项目应避免使用,改用 const 对象 + 字面量联合类型。TS 5.0 也建议默认禁用 isolatedModules 下的 const enum

15. 装饰器两套语法不能混用

TS 5.0 标准装饰器(ECMAScript)与旧的实验性装饰器(experimentalDecorators)语义不同。NestJS、TypeORM 等老生态仍依赖实验性装饰器——切换时需明确 tsconfig 配置,不可同时启用。

16. TS 7.0 是 Go 编译器,旧 API 不再可用

TS 7.0 不提供旧 tsc API(typescript-eslint、webpack loader 等依赖 API 的工具需等 7.1)。如果项目用这些工具,先保持在 6.x(@typescript/typescript6),纯 CLI 构建场景可直接上 7.0。

17. Vue/Svelte/Astro/Angular 模板暂不支持 TS 7

嵌入式语言(模板中的 TS)在 TS 7.0 暂不支持。用这些框架时先停留 6.0;纯 Node/浏览器项目不受影响。

18. 模板字面量类型按 Unicode 码点拆分

TS 7.0 对 \`${infer Head}${infer Tail}\` 的推断改为按 Unicode 码点而非 UTF-16 单元。"😀abc" 现在拆成 "😀""abc",而不是旧的代理对。写字符串 Length 工具或解析 emoji 时注意行为差异。

19. 导入断言改用 with 关键字

TS 7 移除 import ... assert {...},改为 import ... with {...}(与最新 ECMAScript 对齐)。老代码需更新导入 JSON/模块时的断言语法。

🎓17 · 迁移建议与速查

思维转变清单

  1. 放下多线程:不要找 synchronized / Lock / Virtual Thread。用 async/await + Worker + 消息传递。
  2. 放下名义类型:不需要 implements / extends 才能赋值。结构匹配即可——Java 21 record 让"数据类"更接近 TS 的 interface,但名义本质没变。
  3. 放下反射:不要 getDeclaredField / Class.forName。用 keyof + 泛型在编译期搞定。
  4. 放下方法重载:用可选参数 + 默认值替代。
  5. 放下 getter/setter 模板:用构造器参数简写 constructor(public x: T) 自动生成字段,替代 Lombok。
  6. 拥抱 async/await:比 CompletableFuture 链更直观,比回调地狱更可读,单线程无锁。
  7. 拥抱结构化类型:类型即文档,interface + 联合类型表达领域模型——Java 21 的 sealed 是对 TS 联合类型的回应,但 TS 更灵活。
  8. 拥抱函数式集合操作:filter/map/reduce 替代 for 循环 + Stream。

概念映射速查表

JavaTypeScript
class + extendsclass + extends
interface + implementsinterface(隐式实现,结构化)
record(16+)interface / type 别名
sealed(17+)type 联合类型 + 穷尽性检查
switch expression(14+)无(用 switch + return 模拟)
pattern matching(21+)可辨识联合 + 字面量收窄
text block(15+)模板字符串 `...`(可插值)
virtual thread(21+)async/await + Worker(异步,非线程)
enumenumconst 对象 + 字面量联合类型
int / long / doublenumber(统一)/ bigint
String(引用类型)string(值,不可变)
nullnull + undefined
Optional<T>T | null + ?? + ?.
方法重载可选参数 + 默认值
finalconst / readonly
@Override 注解override 关键字
instanceof + pattern(21+)instanceof / typeof / 可辨识联合
var 局部推断(10+)默认推断,无需关键字
ArrayListT[] / Array<T>
HashMapMap<K, V>
HashSetSet<T>
Stream APIArray.filter/map/reduce
CompletableFuturePromise + async/await
ExecutorService / Virtual ThreadWorker(并行)/ async(异步)
synchronized / Lock不需要(单线程,无共享内存竞争)
反射(Field/Method)keyof + 泛型 + 装饰器
注解(Annotation)装饰器(Decorator,TS 5.0 标准)
System.out.printlnconsole.log
Maven / Gradlepnpm / npm / Bun
javactsc 7(Go 原生,8~12x 加速
JVM 字节码JavaScript(编译产物)
JUnit 5Vitest / Jest
Lombok @Data不需要(构造器参数简写 + interface)
javac --releasetsc --target(TS 7 默认当前 ES)
module-info / JPMSimport/export(ESM)

推荐学习路径

  1. 官方文档 TypeScript Handbook —— 类型基础 + 高级类型
  2. 搭建 Node.js 20+ + TS 7.0 环境 —— 跑通 Go 原生 tsc 编译,感受 8~12x 加速与 strict 默认
  3. 类型系统 —— interface、联合类型、泛型、keyof、satisfies
  4. 异步编程 —— Promise、async/await、并发
  5. 工程实践 —— tsconfig 7.0 新默认值、ESLint/Biome、Vitest
  6. 框架选型 —— 后端 NestJS(注意 TS7 API 待 7.1)/ 前端 React + TS / 全栈 Next.js
  7. 实战项目 —— 用 TS 重写一个你熟悉的 Java 21 服务,逐步体会差异
✅ 最终忠告

TypeScript 最大的门槛不是语法——而是"类型即安全" + "单线程异步"的思维转变。好消息是:Java 21+ 的 recordsealedswitch expression、pattern matching 让 Java 越来越像 TS;而 TS 7.0 用 Go 重写编译器、默认 strict,让"编译期类型安全"的体验甚至比 javac 更严更爽。你多年积累的 OOP、设计模式、分层架构经验全部适用。要放下的只是"多线程 + 锁"(含 Virtual Thread 仍是线程模型)和"名义类型强制"这两件事。一旦适应,你会发现 TS 的编译期类型检查 + 结构化类型 + async/await,让代码既安全又简洁。