☰
TypeScript 扩展语法实战指南:从基本类型、接口与类到泛型、只读特性与可辨识联合(learnxinyminutes-docs)
2026/10/10 5:29:53 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】learnxinyminutes-docs

Code documentation written as code! How novel and totally my idea!

项目地址:https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs
点击查看免费下载

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 等为其他语言版本。

如果你想动手实践文中的所有示例,两种方式任选:

  1. 复制本文代码块到 TypeScript Playground 在线运行,实时查看编译产物;
  2. 在本地安装 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!

项目地址:https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs
点击查看免费下载

相关推荐

上一篇:Snyk CLI与其他安全工具对比:优势与适用场景分析
下一篇:PTerm常见陷阱:避免在开发过程中犯的10个典型错误

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询