- 文档
- 教程
【免费下载链接】learnxinyminutes-docs
Code documentation written as code! How novel and totally my idea!
TypeScript 是 JavaScript 的超集(superset),通过引入可选的静态类型、类、模块、接口与泛型等概念,让大型 JavaScript 项目的开发与维护变得更容易。本文以 learnxinyminutes-docs 仓库中的 th/typescript.md(泰语版)及仓库根目录的 typescript.md(英文原版)为核心骨架,聚焦 TypeScript独有的扩展语法(即 JavaScript 之外的语法部分),逐项讲解基础类型、函数、接口、类、模块、泛型、模板字符串、readonly、可辨识联合类型与迭代器,并给出完整可运行的代码示例。读完本文,你将能独立读懂并书写带类型注解的 TypeScript 代码,理解其编译到 JavaScript 的原理,并掌握类型安全边界下的常用模式。
引言:为什么需要 TypeScript,以及本文的讲解范围
TypeScript 的目标是让"用 JavaScript 编写大规模软件"这件事变得更简单。它向 JavaScript 中加入了常见编程语言概念:classes(类)、modules(模块)、interfaces(接口)、generics(泛型)以及可选的静态类型(static typing)。由于它是 JavaScript 的超集,任何合法的 JavaScript 代码同时也是合法的 TypeScript 代码,因此你可以无缝地将 TypeScript 引入既有项目——TypeScript 编译器最终会把代码编译输出为 JavaScript。
与 javascript.md 这类讲解 JavaScript 本身语法的文章不同,本文只关注 TypeScript 的扩展语法部分,默认读者已熟悉 JavaScript 基础。想要快速试玩编译器,可以访问官方 TypeScript Playground——在那里输入代码即可获得自动补全(autocomplete),并实时看到它被转换成什么样的 JavaScript 输出。
基本类型与类型注解
TypeScript 提供 3 种最基本的原始数据类型,通过冒号: 类型的形式进行注解:
// TypeScript 有 3 种基本数据类型 let isDone: boolean = false; let lines: number = 42; let name: string = "Anders";当你用明确字面量(explicit literal)初始化变量时,可以省略类型注解,编译器会自动推断(type inference)出类型:
// 也可以省略类型注解,变量类型会依据给定值自动调整 let isDone = false; let lines = 42; let name = "Anders";当无法确定类型时,可以显式标注为any类型——它表示"任意类型",关闭对该变量的类型检查:
// 无法确定类型时,可以使用 "Any" 类型 let notSure: any = 4; notSure = "maybe a string instead"; notSure = false; // 没问题,现在它确定是个 boolean对于常量,使用const关键字。一旦赋值后再次赋值会直接报错:
// 使用 const 关键字创建常量 const numLivesForCat = 9; numLivesForCat = 1; // Error集合类型:类型化数组与泛型数组
对于集合(collection),TypeScript 支持"类型化数组"(typed arrays)和"泛型数组"(generic arrays)两种等价写法:
let list: number[] = [1, 2, 3]; // 另一种写法:使用泛型数组类型 let list: Array<number> = [1, 2, 3];枚举(Enumerations)
枚举用于定义一组有名字的常量集合,从 0 开始自动编号:
enum Color { Red, Green, Blue }; let c: Color = Color.Green;英文原版 typescript.md 中补充了反向映射示例:console.log(Color[c]); // "Green"——因为 TS 枚举编译后会生成一个同时支持名字→值和值→名字的双向映射对象。
void:表示"函数不返回任何值"
void用于特殊场景:标注一个不返回任何值的函数:
function bigHorribleAlert(): void { alert("I'm a little annoying box!"); }函数:一等公民、胖箭头语法与类型推断
在 TypeScript 中,函数是一等公民(first class citizens),支持 lambda 风格的"胖箭头"(fat arrow)语法,并且可以借助类型推断省去返回类型标注。下面 5 种写法在编译器眼中签名相同、产出的 JavaScript 也相同,本质上是同一函数的不同书写风格:
// 以下风格彼此等价,编译器会推断出相同签名,并生成相同的 JavaScript // 风格 1:普通函数表达式 + 显式返回类型 let f1 = function (i: number): number { return i * i; } // 风格 2:返回类型由编译器推断 let f2 = function (i: number) { return i * i; } // 风格 3:"Fat arrow" 胖箭头 + 显式返回类型 let f3 = (i: number): number => { return i * i; } // 风格 4:"Fat arrow" 胖箭头 + 返回类型推断 let f4 = (i: number) => { return i * i; } // 风格 5:"Fat arrow" 胖箭头 + 返回类型推断 + 无大括号(意味着不需要 return 关键字) let f5 = (i: number) => i * i;英文原版中还补充了联合类型参数的写法——函数参数可以同时接受多种类型,用|分隔:
function f6(i: string | number): void { console.log("The value was " + i); }接口:结构化的类型约定
接口(Interface)描述的是对象"应有的结构":一个对象只要具备接口要求的属性,就与该接口兼容。这种基于形状(shape)而非继承关系的类型检查,正是 TypeScript 结构化类型系统(structural typing)的核心。
interface Person { name: string; // 可选属性(Optional properties),用 "?" 标记 age?: number; // 接口中当然也可以声明函数 move(): void; }下面的对象只要拥有name和move属性,即可被视为Person类型:
// 这个对象实现了 "Person" 接口,因此它是 Person 类型 let p: Person = { name: "Bobby", move: () => { } }; // 带有可选属性 age 的对象同样是 Person 类型 let validPerson: Person = { name: "Bobby", age: 42, move: () => { } }; // 这不是 Person:因为接口要求 age 必须是 number,而这里给了 boolean let invalidPerson: Person = { name: "Bobby", age: true };函数类型接口
接口还可以用来描述"函数类型",此时它相当于一个函数签名(签名类型):
interface SearchFunc { (source: string, subString: string): boolean; }赋值时只有参数的类型重要,参数名字不必一致:
// 只有参数类型重要,参数名无需一致 let mySearch: SearchFunc; mySearch = function (src: string, sub: string) { return src.search(sub) != -1; }类:属性、构造器、静态成员与继承
TypeScript 的类成员默认是 public。类的完整示例:
class Point { // Properties 属性 x: number; // Constructor 构造器 // 在这里使用 public/private 关键字,会自动生成对应属性及其在构造器中的初始化代码(样板代码) // 本例中 "y" 的声明方式与 "x" 相同,但写法更简洁 // 同时参数支持默认值 constructor(x: number, public y: number = 0) { this.x = x; } // 函数成员 dist(): number { return Math.sqrt(this.x * this.x + this.y * this.y); } // 静态成员(static members) static origin = new Point(0, 0); }注意构造器签名constructor(x: number, public y: number = 0)中public y的写法:它等价于"声明y属性并在构造器中用传入值初始化",同时= 0提供了默认值。这是 TypeScript 提供的构造器参数属性(parameter properties)语法糖。
类实现接口
类可以显式声明实现某个接口(implements)。如果实现中缺少接口要求的成员,编译期就会报错:
class PointPerson implements Person { name: string move() {} }(此处若name未初始化,会提示错误;正确的做法是通过构造器接收参数传入。)
实例化与继承
let p1 = new Point(10, 20); let p2 = new Point(25); // y 使用默认值 0继承使用extends,派生类构造器中必须显式调用super(...)来初始化父类:
class Point3D extends Point { constructor(x: number, y: number, public z: number = 0) { super(x, y); // 必须显式调用 super 类的构造器 } // 覆盖(Overwrite)Point 中已有的 dist() 方法 dist(): number { let d = super.dist(); return Math.sqrt(d * d + this.z * this.z); } }这里演示了如何用super.dist()复用父类方法,再叠加z维度计算三维距离。
模块与命名空间
TypeScript 早期使用module关键字组织代码(现代版本中更推荐namespace或 ES Moduleimport/export)。模块用于将类分组,可以用.作为子模块分隔符,形成类似Module.Class的访问路径:
module Geometry { export class Square { constructor(public sideLength: number = 0) { } area() { return Math.pow(this.sideLength, 2); } } } let s1 = new Geometry.Square(5);可以用import ... = ...为模块创建局部别名,像 Python 等语言那样简化引用:
// 创建指向模块的本地别名 import G = Geometry; let s2 = new G.Square(10);泛型(Generics)
泛型让类、接口和函数能够"延迟指定"类型,在使用时再传入具体类型,从而在保持类型安全的同时复用逻辑。
泛型类
class Tuple<T1, T2> { constructor(public item1: T1, public item2: T2) { } }泛型接口
interface Pair<T> { item1: T; item2: T; }泛型函数
// 泛型函数:<T> 声明在函数名前 let pairToTuple = function <T>(p: Pair<T>) { return new Tuple(p.item1, p.item2); }; let tuple = pairToTuple({ item1: "hello", item2: "world" });上例中,pairToTuple接收一个Pair<T>,返回Tuple<T1, T2>,类型参数T会在调用时由编译器依据实参自动推断(此处推断为string)。
引用类型声明文件(Definition Files)
当你需要引用第三方库(如 jQuery)的类型声明文件(.d.ts)时,可以使用三斜线指令:
/// <reference path="jquery.d.ts" />这类声明文件只包含类型信息、不含实现,编译后不会产生任何 JavaScript 输出,是让 TS 认识既有 JS 库的桥梁。
模板字符串(Template Strings)
模板字符串使用反引号(backtick,键盘左上角`键)包裹,支持字符串插值与多行字符串:
// 字符串插值:${expression} 会替换为表达式的值 let name = 'Tyrone'; let greeting = `Hi ${name}, how are you?` // 多行字符串:直接换行书写 let multiline = `This is an example of a multiline string`;只读特性 readonly(TypeScript 3.1 新特性)
readonly修饰符使属性一经初始化便不可再赋值。注意它修饰的是"只读属性"而非"深层不可变"——对象内部的成员仍可能变化,且只读约束在编译期生效。
interface Person { readonly name: string; readonly age: number; } var p1: Person = { name: "Tyrone", age: 42 }; p1.age = 25; // Error,p1.age 是只读的只读还支持"别名共享"的语义:把一个普通对象赋给只读接口类型后,通过别名仍可能改变原对象的值,但通过只读引用赋值会报错:
var p2 = { name: "John", age: 60 }; // 注意 p2 并未声明为 Person var p3: Person = p2; // 可以:p3 是 p2 的只读别名,并声明为 Person 类型 p3.age = 35; // Error,p3.age 是只读的 p2.age = 45; // 可以:但会同时改变 p3.age,因为 p3 是 p2 的别名泰语原文档中此例写作p3.x/p2.x(与age混用),此处按英文原版 typescript.md 的age版本呈现,语义一致。
类中同样可以使用readonly。在构造器内允许为只读属性赋值,构造器之外则不允许:
class Car { readonly make: string; readonly model: string; readonly year = 2018; constructor() { this.make = "Unknown Make"; // 允许:在构造器中为只读属性赋值 this.model = "Unknown Model"; // 允许:在构造器中为只读属性赋值 } }ReadonlyArray:只读数组
ReadonlyArray<T>是数组的只读视图:不能修改元素、不能调用会改变数组的方法(如push)、不能修改length,甚至不能把只读数组赋值回可变数组:
let numbers: Array<number> = [0, 1, 2, 3, 4]; let moreNumbers: ReadonlyArray<number> = numbers; moreNumbers[5] = 5; // Error,数组元素是只读的,禁止修改 moreNumbers.push(5); // Error,不存在 push 方法(因为它会改变数组) moreNumbers.length = 3; // Error,length 也是只读的 numbers = moreNumbers; // Error,缺少可变方法,类型不兼容可辨识联合类型(Tagged Union Types)
当状态(state)可能处于多种不同形状时,可辨识联合类型是最佳建模方式。它通过每个成员共有的字面量类型字段(这里的type)作为"判别标签"(tag),配合控制流收窄(narrowing)实现精确的类型检查:
type State = | { type: "loading" } | { type: "success", value: number } | { type: "error", message: string }; declare const state: State; if (state.type === "success") { console.log(state.value); // 此处 state 被收窄为 success 分支,value 可用 } else if (state.type === "error") { console.error(state.message); // 此处 state 被收窄为 error 分支,message 可用 }通过if/else对判别字段进行判断后,编译器会在每个分支内自动收窄类型,从而在分支中安全访问该形状独有的属性——这是现代 TypeScript 处理异步加载、接口请求等状态机的常用模式。
模板字面量类型(Template Literal Types)
英文原版 typescript.md 还收录了模板字面量类型:利用反引号与${}组合字符串字面量类型,构造出"受限的字符串类型":
type OrderSize = "regular" | "large"; type OrderItem = "Espresso" | "Cappuccino"; type Order = `A ${OrderSize} ${OrderItem}`; let order1: Order = "A regular Cappuccino"; let order2: Order = "A large Espresso"; let order3: Order = "A small Espresso"; // Error,small 不在 OrderSize 中它把字符串模板的语法引入类型层面,适合建模格式受限的字符串(如路由路径、消息模板等)。
迭代与生成器:for..of 与 for..in
for..of:遍历值
for..of遍历的是被迭代对象上的值列表(values):
let arrayOfAnyType = [1, "string", false]; for (const val of arrayOfAnyType) { console.log(val); // 1, "string", false } let list = [4, 5, 6]; for (const i of list) { console.log(i); // 4, 5, 6 }for..in:遍历键
for..in遍历的是被迭代对象上的键列表(keys):
for (const i in list) { console.log(i); // 0, 1, 2 }对比两个例子:对数组使用for..of得到元素值(4,5,6),使用for..in则得到索引键("0","1","2")——这也是面试与日常编码中极易混淆的一对语法。
类型断言(Type Assertion)
英文原版还补充了类型断言:当编译器把变量推断为过窄的类型时,可以用as断言成更宽泛的接口类型,从而允许添加属性:
let foo = {} // foo 被推断为空对象类型 {} foo.bar = 123 // Error:类型 {} 上不存在属性 'bar' foo.baz = 'hello world' // Error:类型 {} 上不存在属性 'baz'因为foo被推断为{}(零属性的对象类型),不允许直接添加bar、baz。使用类型断言后即可通过:
interface Foo { bar: number; baz: string; } let foo = {} as Foo; // 类型断言:把 {} 断言为 Foo foo.bar = 123; foo.baz = 'hello world'注意:类型断言是编译期概念,不会改变运行时行为,只用于"你比编译器更清楚这个值的真实形状"的场景。
在 learnxinyminutes-docs 仓库中如何定位与使用本文
learnxinyminutes-docs 是一个以"可执行的带注释代码"形式速览编程语言的开源文档仓库,每个语言文档都是一个独立的 Markdown 文件,遵循统一的 YAML frontmatter 规范(见 lint/frontmatter.py,允许的元数据键包括name、category、filename、contributors、translators等,并由 lint/frontmatter.py 校验)。仓库按语言代码分目录存放翻译版本,其中:
- th/typescript.md 为本文依据的泰语翻译版(由 Philippe Vlérick 与 Worajedt Sitthidumrong 贡献);
- typescript.md 为英文原版,内容更全(额外包含联合类型参数、模板字面量类型与类型断言);
- zh-cn/typescript.md、es/typescript.md、fr/typescript.md、it/typescript.md、pt-br/typescript.md、ru/typescript.md、uk/typescript.md、vi/typescript.md 等为其他语言版本。
如果你想动手实践文中的所有示例,两种方式任选:
- 复制本文代码块到 TypeScript Playground 在线运行,实时查看编译产物;
- 在本地安装 TypeScript 编译器(
npm install -g typescript),将代码保存为.ts文件后用tsc编译,观察生成的 JavaScript 输出。
进一步学习
- 官方 TypeScript 网站:https://www.typescriptlang.org/(含 Playground 与官方文档)
- TypeScript 源码:https://github.com/microsoft/TypeScript
- 英文原版与各语言译本:typescript.md 及
zh-cn/、es/、fr/、it/、pt-br/、ru/、uk/、vi/等目录下的同名文件 - 仓库贡献指南:CONTRIBUTING.md(含文档格式规范),项目整体说明见 README.md
- 文档
- 教程
【免费下载链接】learnxinyminutes-docs
Code documentation written as code! How novel and totally my idea!
相关推荐
learnxinyminutes-docs 之 TypeScript 语法速成:从基础类型、接口到泛型与只读约束的完整实战解析
learnxinyminutes docs 之 TypeScript 语法速成:从基础类型、接口到泛型与只读约束的完整实战解析 本篇文章以 learnxinym
文档教程TypeScript 语法速成实战指南:从基本类型到类、模块与泛型(learnxinyminutes-docs 仓库解读)
TypeScript 语法速成实战指南:从基本类型到类、模块与泛型(learnxinyminutes docs 仓库解读) TypeScript 是面向大型 J
文档教程TypeScript 联合类型(Union Type)实战指南:从 `|` 语法到可辨识联合与穷尽性检查
TypeScript 联合类型(Union Type)实战指南:从 | 语法到可辨识联合与穷尽性检查 联合类型(Union Type)是 TypeScript
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考