TypeScript 与 JavaScript 语法速览(Python 对照)
这篇笔记写给已掌握 Python、现在要读和写 TypeScript 代码的开发者。它不是从零教编程,而是帮你把 Python 的语法直觉翻译成 JS/TS 的等价写法——聚焦那些看起来像但行为不同、或者Python 里根本没有的语言机制。
TypeScript 是 JavaScript 的超集——所有 JS 语法在 TS 中完全有效。所以这篇笔记混用 JS 和 TS 示例:JS 部分对应运行时语法,TS 类型注解(冒号后面的东西)在后续章节展开。
1. 变量声明:const / let / var vs Python 赋值
Python 的赋值即声明;JS/TS 必须先声明再使用。
# Python:赋值就是声明
x = 42
x = "hello" # 随时可以变类型// TS:const(不可重新赋值)vs let(可重新赋值)
const x = 42; // 不可 re-assign
// x = 43; // 编译错误
let y = 42; // 可重新赋值
y = 43;
// 类型注解(可选,靠推断)
const name: string = "Alice";
let age: number = 30;
// var(有作用域陷阱,永远不用)
var z = 42; // 函数作用域,不是块作用域| 关键词 | 可重新赋值? | 作用域 | 与 Python 对比 |
|---|---|---|---|
const | ❌ | 块级 {} | 类似赋值后永不改的变量 |
let | ✅ | 块级 {} | 最接近 Python 的普通变量 |
var | ✅ | 函数级 | 不用——提升行为是陷阱 |
Python 的变量可以随时改类型;TS 的变量一旦推断出类型就固定了。
const只是禁止=重新赋值,不禁止.push()修改数组内容。
2. 函数:五种写法 vs Python 的 def / lambda
# Python:两种函数形式
def add(a, b):
return a + b
add = lambda a, b: a + b # 只能单表达式,不能有语句// TS:五种函数形式
// 1. 函数声明(会被提升)
function add(a: number, b: number): number {
return a + b;
}
// 2. 函数表达式
const add = function(a: number, b: number): number {
return a + b;
};
// 3. 箭头函数(最常用,没有自己的 this)
const add = (a: number, b: number): number => a + b;
// 4. 箭头函数 + 语句体
const add = (a: number, b: number): number => {
const result = a + b;
return result;
};
// 5. 方法简写(对象内)
const obj = {
add(a: number, b: number): number {
return a + b;
},
};| 维度 | Python def | Python lambda | TS 箭头函数 | TS function |
|---|---|---|---|---|
| 可含语句 | ✅ | ❌ | ✅ | ✅ |
有 this 绑定 | ❌ 显式 self | ❌ | ❌(词法 this) | ✅ 动态 this |
| 可做回调 | 可以 | 常规用法 | ✅ 最常用 | 可以 |
习惯转换:Python 里你用
def定义一切;TS 里回调优先箭头函数(items.map(x => x * 2)),顶层逻辑用function或箭头函数均可。
3. 对象与解构 vs Python 的 dict
JS 的对象是核心数据结构——它同时承担 Python 里 dict、dataclass、NamedTuple 的角色。
# Python
user = {"name": "Alice", "age": 30}
name = user["name"]
age = user["age"]
# Python 3.10+ 结构匹配(类似但不等于解构)
match user:
case {"name": n, "age": a}:
print(n, a)// TS:对象字面量 + 解构
const user = { name: "Alice", age: 30 };
// 解构(destructuring)——极常用
const { name, age } = user;
console.log(name, age); // Alice 30
// 重命名 + 默认值
const { name: userName, email = "[email protected]" } = user;
// 函数参数解构
function greet({ name, age }: { name: string; age: number }) {
return `${name} is ${age}`;
}
// 展开(spread)——创建浅拷贝 + 覆盖
const updated = { ...user, age: 31 };
// { name: "Alice", age: 31 }| 操作 | Python | TS/JS |
|---|---|---|
| 访问属性 | d["key"] 或 d.key(dataclass) | obj.key 或 obj["key"] |
| 解构 | match case 或手动拆 | const { a, b } = obj |
| 合并 | {**d1, **d2} | { ...obj1, ...obj2 } |
| 判断键存在 | "key" in d | "key" in obj |
4. this vs Python 的 self
这是 Python 开发者最容易踩的坑。
# Python:self 是显式参数,绑定由调用方式决定
class Button:
def __init__(self, text):
self.text = text
def handle(self):
print(self.text) # self 永远指向接收者
btn = Button("Click")
btn.handle() # self = btn
cb = btn.handle
cb() # Python 3 里 self 仍是 btn(绑定方法)// TS/JS:this 是隐式的,由"谁调用"决定
class Button {
text: string;
constructor(text: string) {
this.text = text;
}
handle() {
console.log(this.text);
}
}
const btn = new Button("Click");
btn.handle(); // this = btn
const cb = btn.handle;
cb(); // this = undefined(严格模式)或 window!
// 修复方式:箭头函数(词法 this)
class ButtonFixed {
text: string;
constructor(text: string) {
this.text = text;
}
handle = () => {
console.log(this.text); // 箭头函数不绑定自己的 this
};
}| 维度 | Python | TS/JS |
|---|---|---|
| 声明 | 显式 self 参数 | 隐式 this |
| 绑定时机 | 方法对象创建时(描述符协议) | 调用时 |
| 回调安全性 | ✅ 绑定方法自动带 self | ❌ 裸函数丢失 this |
| 修复手段 | 不需要 | 箭头函数 / .bind() |
TS 里回调函数优先箭头函数——不是为了简洁,是为了
this不会丢。
5. 相等性:=== vs == vs Python
# Python:== 是值相等,类型不匹配直接返回 False
0 == "0" # False
0 == False # False(Python 3)
[] == False # False
None == False # False// JS:== 会做类型转换(永远不要用)
0 == "0"; // true (wtf — 字符串被转成数字)
0 == false; // true (wtf — 布尔被转成数字 0)
"" == false; // true (wtf — 两者都被转为 0)
null == undefined; // true
// === 不做类型转换(永远用这个)
0 === "0"; // false
0 === false; // false
null === undefined; // false| 运算符 | Python | TS/JS |
|---|---|---|
| 宽松比较 | (无) | ==(做类型转换,别用) |
| 严格比较 | == | ===(值 + 类型都相等) |
| 不等 | != | !==(严格不等) |
忘记 JS 有
==和!=,只用===和!==。
6. 空值:null vs undefined vs Python None
# Python:只有一个空值
x = None
name = None
# 判断空
if x is None:
print("空")// TS:两个空值,语义不同
let x: null = null; // 有意设为"没有值"
let y: undefined = undefined; // 变量声明了但未赋值
// 不赋值的变量默认是 undefined
let z: string; // z 的值是 undefined
// 判断空
if (x === null) { /* ... */ }
if (y === undefined) { /* ... */ }
// 判断"非空"(排除 null 和 undefined 两者)
if (value != null) { /* ... */ } // 这是 == 唯一被容忍的用法
// ?? 空值合并(null/undefined 时用默认值)
const name = input ?? "Anonymous"; // null / undefined → "Anonymous"
// ?. 可选链(null/undefined 时不报错,返回 undefined)
const city = user?.address?.city; // 任一环节为 null/undefined → undefined| 概念 | Python | TS/JS |
|---|---|---|
| 空值 | None(一个) | null + undefined(两个) |
| 默认值 | x or default(但不安全) | x ?? default |
| 安全访问 | getattr(obj, "attr", None) | obj?.attr |
Python 直觉迁移:把
null看作None;undefined是 JS 特有的——大多数时候你只需要检查”非 null 且非 undefined”,用!= null或??。
7. 模板字面量 vs f-string
# Python
name = "Alice"
s = f"Hello, {name}!"
multi = f"""
你好,{name}
欢迎回来
"""// TS:模板字面量(反引号,不是引号)
const name = "Alice";
const s = `Hello, ${name}!`; // 注意:反引号
// 多行(保留缩进和换行,与 Python f-string 行为不同)
const multi = `
你好,${name}
欢迎回来
`;
// 带标签的模板(高级用法)
const raw = String.raw`C:\Users\${name}\docs`; // 反斜杠不转义| 维度 | Python f-string | TS 模板字面量 |
|---|---|---|
| 分隔符 | f"..." 或 f'...' | `...`(反引号) |
| 插值 | {expr} | ${expr} |
| 多行 | """...""" 自动去缩进 | 严格保留所有空白 |
8. 迭代:for...of / .map() / .forEach() vs Python
# Python
items = [1, 2, 3]
# 遍历值
for item in items:
print(item)
# 生成新列表
doubled = [x * 2 for x in items]
# 带索引
for i, item in enumerate(items):
print(i, item)// TS:四种主流迭代方式
const items = [1, 2, 3];
// 1. for...of(遍历值——最接近 Python for...in)
for (const item of items) {
console.log(item);
}
// 2. .map()(生成新数组——最常用)
const doubled = items.map(x => x * 2);
// 3. .forEach()(纯副作用,不返回值)
items.forEach((item, index) => {
console.log(index, item);
});
// 4. for...in(遍历键/索引——通常不是你想要的)
for (const index in items) { // index 是 "0", "1", "2"(字符串!)
console.log(index, items[index]);
}| 需求 | Python | TS/JS |
|---|---|---|
| 遍历值 | for x in items | for (const x of items) |
| 生成新列表 | [f(x) for x in items] | items.map(x => f(x)) |
| 带索引遍历 | enumerate(items) | .forEach((x, i) => ...) |
| 过滤 | [x for x in items if p(x)] | items.filter(x => p(x)) |
for...in在 JS 里遍历的是键名(字符串),不是值。除非你明确需要对象的键,否则用for...of。
9. 模块导入导出 vs Python
# Python
import os
from collections import defaultdict
from .utils import helper # 相对导入
import numpy as np// TS:三种导入风格混合(取决于模块系统和打包器)
// 1. ESM(现代标准,Node 23+ / Vite 默认)
import fs from "node:fs"; // 默认导入
import { readFile } from "node:fs"; // 命名导入
import * as fs from "node:fs"; // 命名空间导入
import type { User } from "./types"; // 仅类型导入(编译后消失)
// 2. CJS(旧 Node 代码,正在消退)
const fs = require("fs");
// 导出
export default User; // 默认导出(一个文件一个)
export { User, createUser }; // 命名导出(多个)
export type { User }; // 仅类型导出| 操作 | Python | TS/JS (ESM) |
|---|---|---|
| 默认导入 | ❌ 不区分 | import X from "./x" |
| 命名导入 | from x import a, b | import { a, b } from "./x" |
| 全部导入 | import x | import * as x from "./x" |
| 重命名 | import numpy as np | import { longName as L } from "./x" |
| 类型导入 | (不需要区分) | import type { User } from "./x" |
10. Spread / Rest 运算符 ...
# Python:* 和 ** 打包/解包
def f(a, b, c):
return a + b + c
args = [1, 2, 3]
f(*args) # 解包列表为位置参数
def g(**kwargs):
print(kwargs)
g(name="Alice", age=30) # 打包关键字参数为 dict// TS:... 统一处理数组和对象的展开/收集
// Rest:收集剩余参数
function f(a: number, ...rest: number[]): number {
return a + rest.reduce((sum, n) => sum + n, 0);
}
f(1, 2, 3, 4); // a=1, rest=[2,3,4]
// Spread:展开
const nums = [1, 2, 3];
const moreNums = [...nums, 4, 5]; // [1, 2, 3, 4, 5]
// 对象展开(没有 Python 对应)
const defaults = { theme: "light", lang: "zh" };
const overrides = { lang: "en" };
const config = { ...defaults, ...overrides }; // { theme: "light", lang: "en" }
...在函数参数位置是 Rest(收集),在调用/赋值位置是 Spread(展开)。
11. Truthy/Falsy:JS 的隐式布尔转换
# Python:只有少数值是 falsy
bool(None) # False
bool(0) # False
bool("") # False
bool([]) # False
bool({}) # False
# 其他都是 truthy// JS/TS:falsy 值的列表更长且包含陷阱
// Falsy 值:false, 0, -0, 0n, "", null, undefined, NaN
// 其他都是 truthy(包括空数组和空对象!)
Boolean(null); // false
Boolean(undefined); // false
Boolean(0); // false
Boolean(""); // false
Boolean(NaN); // false
Boolean([]); // true ⚠️ 空数组是 truthy!
Boolean({}); // true ⚠️ 空对象是 truthy!
Boolean(" "); // true ⚠️ 空白字符串是 truthy!
// 陷阱:判断数组是否非空
if (arr.length > 0) { } // 明确
if (arr) { } // 空数组也是 true习惯转换:不要用
if (x)来判断数组/对象是否非空——JS 的空数组和空对象是 truthy。用x.length > 0或Object.keys(x).length > 0。
关联
- TS 类型系统 — 在语法基础上叠加类型
- TS 并发与事件模型 — Promise/async 深入
- Python 语法基础 — 本笔记的 Python 参照面
- Python 函数与编程范式 — Python
def/lambda对照 - TypeScript 专题 MOC