typescript:第一章typescript基础

第一章:TypeScript 基础

欢迎来到 TypeScript 的世界!本章将带你从零开始了解 TypeScript,包括安装配置、编译原理以及最基本的类型注解概念。通过本章学习,你将能够搭建 TypeScript 开发环境并编写第一个 TypeScript 程序。

TypeScript 是一种由 Microsoft 开发的开源编程语言,于 2012 年 10 月首次发布。它是 JavaScript 的超集(Superset),意味着任何有效的 JavaScript 代码都是有效的 TypeScript 代码。

静态类型系统 TypeScript 最大的特点是添加了可选的静态类型系统。这使得开发者可以在编码阶段就发现类型错误,而不是在运行时。

javascript
// JavaScript - 运行时才发现错误
function add(a, b) {
  return a + b;
}
add("1", 2); // "12" - 逻辑错误但未报错
 
// TypeScript - 编译时就发现错误
function addTS(a: number, b: number): number {
  return a + b;
}
addTS("1", 2); // Error: Argument of type 'string' is not assignable to parameter of type 'number'

ES6+ 特性支持 TypeScript 支持最新的 ECMAScript 特性,包括:

  1. 类(Class)
  2. 模块(Module)
  3. 异步函数(Async/Await)
  4. 解构赋值
  5. 箭头函数
  6. 等等

编译时转换 TypeScript 代码需要编译(转译)为 JavaScript 才能在浏览器或 Node.js 中运行。编译器会将高级语法转换为兼容目标环境的 JavaScript。

特性 JavaScript TypeScript
类型系统 动态类型 静态类型(可选)
编译 解释执行 需要编译
错误检测 运行时 编译时
IDE 支持 基础 强大
学习曲线 平缓 稍陡
代码量 较少 稍多(类型注解)
维护性 一般 优秀

在开始之前,请确保你的系统已安装:

  1. Node.js(推荐 v18 或更高版本)
  2. npm 或 yarn 包管理器

检查 Node.js 版本:

bash
node --version
npm --version

使用 npm 全局安装 TypeScript 编译器:

# 使用 npm
npm install -g typescript

# 使用 yarn
yarn global add typescript

验证安装:

tsc --version
# 输出类似:Version 5.3.3

对于实际项目,建议在项目本地安装 TypeScript:

# 创建项目目录
mkdir my-ts-project
cd my-ts-project

# 初始化 npm 项目
npm init -y

# 本地安装 TypeScript
npm install --save-dev typescript

# 或作为开发依赖
npm install -D typescript

本地安装后,使用 npx 运行 tsc:

npx tsc --version

创建一个名为 “hello.ts” 的文件:

// hello.ts
function greet(person: string, date: Date): string {
  return `Hello ${person}, today is ${date.toDateString()}!`;
}

const user = "TypeScript";
const today = new Date();

console.log(greet(user, today));

使用 tsc 命令编译文件:

tsc hello.ts

这将生成一个同名的 JavaScript 文件 “hello.js”:

// hello.js (编译输出)
function greet(person, date) {
  return "Hello ".concat(person, ", today is ").concat(date.toDateString(), "!");
}
var user = "TypeScript";
var today = new Date();
console.log(greet(user, today));
node hello.js
# 输出:Hello TypeScript, today is Mon Jan 15 2025!

TypeScript 编译器(tsc)的工作流程:

TypeScript 源码 → 词法分析 → 语法分析 → 语义分析 → 类型检查 → 代码生成 → JavaScript 代码

1. 词法分析(Lexical Analysis) 将源代码转换为 Token 序列。

2. 语法分析(Parsing) 将 Token 序列转换为抽象语法树(AST)。

3. 语义分析(Semantic Analysis) 检查语法结构的语义正确性。

4. 类型检查(Type Checking) 这是 TypeScript 的核心阶段,验证类型系统的约束。

5. 代码生成(Code Generation) 将 AST 转换为目标 JavaScript 代码。

TypeScript 的类型只在编译时存在,编译后的 JavaScript 代码中完全不包含类型信息,这个过程称为“类型擦除”(Type Erasure)。

// TypeScript
interface Point {
  x: number;
  y: number;
}

function distance(p1: Point, p2: Point): number {
  const dx = p1.x - p2.x;
  const dy = p1.y - p2.y;
  return Math.sqrt(dx * dx + dy * dy);
}

编译后:

// JavaScript
function distance(p1, p2) {
  var dx = p1.x - p2.x;
  var dy = p1.y - p2.y;
  return Math.sqrt(dx * dx + dy * dy);
}

注意:接口定义和类型注解在编译后完全消失!

使用冒号(:)为变量添加类型注解:

// 基本类型
let name: string = "Alice";
let age: number = 25;
let isStudent: boolean = true;

// 数组类型
let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ["Alice", "Bob", "Charlie"];

// any 类型 - 绕过类型检查
let anything: any = 4;
anything = "string";
anything = true;

// unknown 类型 - 类型安全的 any
let notSure: unknown = 4;
// notSure.toFixed(); // Error: Object is of type 'unknown'

// void - 无返回值
function logMessage(message: string): void {
  console.log(message);
}

// null 和 undefined
let u: undefined = undefined;
let n: null = null;
// 函数声明
function add(x: number, y: number): number {
  return x + y;
}

// 函数表达式
const multiply = function(x: number, y: number): number {
  return x * y;
};

// 箭头函数
const divide = (x: number, y: number): number => {
  return x / y;
};

// 可选参数
function greet(name: string, greeting?: string): string {
  if (greeting) {
    return `${greeting}, ${name}!`;
  }
  return `Hello, ${name}!`;
}

// 默认参数
function greetWithDefault(name: string, greeting: string = "Hello"): string {
  return `${greeting}, ${name}!`;
}

// 剩余参数
function sum(...numbers: number[]): number {
  return numbers.reduce((total, num) => total + num, 0);
}
// 对象字面量类型
let person: {
  name: string;
  age: number;
  isStudent?: boolean;  // 可选属性
} = {
  name: "Alice",
  age: 25
};

// 使用接口(推荐)
interface Person {
  name: string;
  age: number;
  email?: string;
}

const alice: Person = {
  name: "Alice",
  age: 25,
  email: "alice@example.com"
};
tsc --init

这将创建一个包含所有编译器选项的 tsconfig.json 文件。

{
  "compilerOptions": {
    // 目标 JavaScript 版本
    "target": "ES2020",
    
    // 模块系统
    "module": "ESNext",
    
    // 模块解析策略
    "moduleResolution": "node",
    
    // 输出目录
    "outDir": "./dist",
    
    // 源代码目录
    "rootDir": "./src",
    
    // 启用严格模式
    "strict": true,
    
    // 允许编译 JavaScript 文件
    "allowJs": true,
    
    // 生成 source map
    "sourceMap": true,
    
    // 启用装饰器
    "experimentalDecorators": true,
    
    // 启用 ES 模块互操作
    "esModuleInterop": true,
    
    // 跳过库类型检查
    "skipLibCheck": true,
    
    // 强制文件名大小写一致
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

target 指定编译后的 JavaScript 版本:“ES3”, “ES5”, “ES2015”, “ES2016”, “ES2017”, “ES2018”, “ES2019”, “ES2020”, “ESNext”

module 指定模块系统:“CommonJS”, “AMD”, “System”, “UMD”, “ES2015”, “ES2020”, “ESNext”

strict 启用所有严格类型检查选项的快捷方式,等同于开启:

  1. strictNullChecks
  2. strictFunctionTypes
  3. strictBindCallApply
  4. strictPropertyInitialization
  5. noImplicitAny
  6. noImplicitThis
  7. alwaysStrict

noImplicitAny 禁止隐式的 any 类型,要求必须为没有类型注解的变量或参数指定类型。

TypeScript 具有强大的类型推断能力,在很多情况下不需要显式添加类型注解。

// TypeScript 会自动推断类型
let message = "Hello";  // 推断为 string
let count = 42;         // 推断为 number
let isValid = true;     // 推断为 boolean

// 错误示例
message = 42;  // Error: Type 'number' is not assignable to type 'string'
// 返回值类型可以被推断
function add(a: number, b: number) {
  return a + b;  // 推断返回值为 number
}

// 复杂推断
function getArray() {
  return [1, 2, 3];  // 推断为 number[]
}

应该添加类型注解的情况:

  1. 函数参数
  2. 函数返回值(复杂逻辑时)
  3. 类的公共属性
  4. 需要明确类型的变量

可以依赖推断的情况:

  1. 简单变量的初始化
  2. 明显能从上下文推断的类型
  3. 局部变量
// 推荐:为函数参数和返回值添加类型
function calculateTotal(price: number, quantity: number): number {
  return price * quantity;
}

// 推荐:复杂对象明确类型
interface User {
  id: number;
  name: string;
}

const users: User[] = fetchUsers();

// 可选:简单变量可以依赖推断
const total = calculateTotal(100, 5);
const welcomeMessage = "Welcome!";

类型断言(Type Assertion)允许你告诉编译器某个值的具体类型,类似于其他语言中的类型转换。

// 尖括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

// as 语法(推荐,特别是在 JSX 中)
let strLength2: number = (someValue as string).length;
// 处理 DOM 元素
const input = document.getElementById("user-input") as HTMLInputElement;
console.log(input.value);

// 处理 API 响应
interface User {
  name: string;
  age: number;
}

const response: any = fetchUser();
const user = response as User;

// 双重断言(谨慎使用)
const something = "hello" as unknown as number;  // 强制转换

TypeScript 允许将字面量作为类型使用,提供更精确的类型约束。

// 字符串字面量类型
let direction: "north" | "south" | "east" | "west";
direction = "north";  // ✓
// direction = "up";  // ✗ Error

// 数字字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
let roll: DiceRoll = 4;  // ✓
// let invalid: DiceRoll = 7;  // ✗ Error

// 布尔字面量类型
type Status = true | false;

// 结合使用
interface Config {
  mode: "development" | "production" | "test";
  port: 3000 | 8080 | 9000;
  debug: boolean;
}
// strictNullChecks: false(默认)
let name: string = null;  // OK

// strictNullChecks: true
let name2: string = null;  // Error: Type 'null' is not assignable to type 'string'

// 正确使用
let name3: string | null = null;  // OK
// noImplicitAny: false
function log(message) {  // 参数隐式为 any
  console.log(message);
}

// noImplicitAny: true
function log2(message: any) {  // 必须显式声明 any
  console.log(message);
}

本章我们学习了:

1. TypeScript 基础概念 - 理解 TypeScript 是什么以及它与 JavaScript 的关系

2. 环境搭建 - 安装 TypeScript 编译器并创建第一个程序

3. 编译原理 - 了解 TypeScript 如何编译为 JavaScript

4. 类型注解 - 掌握基本类型的使用方法

5. tsconfig.json - 配置编译器选项

6. 类型推断 - 理解何时需要显式添加类型

7. 类型断言 - 在必要时覆盖类型推断

为以下变量和函数添加适当的类型注解:

// 待补充类型的代码
let userName = "张三";
let userAge = 25;
let hobbies = ["阅读", "编程", "游戏"];

function greetUser(name, age) {
  return `你好,${name},今年${age}岁`;
}

function calculateArea(width, height) {
  return width * height;
}

参考答案

let userName: string = "张三";
let userAge: number = 25;
let hobbies: string[] = ["阅读", "编程", "游戏"];

function greetUser(name: string, age: number): string {
  return `你好,${name},今年${age}岁`;
}

function calculateArea(width: number, height: number): number {
  return width * height;
}

创建一个适合前端项目的 tsconfig.json 配置,要求:

  1. 目标 ES2018
  2. 使用 ES 模块
  3. 输出到 dist 目录
  4. 源代码在 src 目录
  5. 启用严格模式
  6. 生成 source map

参考答案

{
  "compilerOptions": {
    "target": "ES2018",
    "module": "ESNext",
    "moduleResolution": "node",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "sourceMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

分析以下代码,指出哪些地方使用了类型推断,哪些地方需要类型断言:

const apiResponse = fetchData();
const userName = apiResponse.name;
const userAge = apiResponse.age;

const element = document.getElementById("input");
const value = element.value;

参考答案与解释

const apiResponse: any = fetchData();  // 需要类型,默认推断为 any
const userName: string = apiResponse.name;  // 从上下文推断,或需要断言
const userAge: number = apiResponse.age;

// 需要类型断言,因为 getElementById 返回 HTMLElement | null
const element = document.getElementById("input") as HTMLInputElement;
const value = element.value;

// 或者先进行类型检查
const element2 = document.getElementById("input");
if (element2 instanceof HTMLInputElement) {
  const value2 = element2.value;  // 这里 TypeScript 知道类型
}

使用字面量类型定义一个配置对象,要求:

  1. 环境只能是 “dev”, “test”, “prod” 之一
  2. 日志级别只能是 “debug”, “info”, “warn”, “error” 之一
  3. 端口号只能是 3000, 8080, 9000 之一

参考答案

type Environment = "dev" | "test" | "prod";
type LogLevel = "debug" | "info" | "warn" | "error";
type Port = 3000 | 8080 | 9000;

interface AppConfig {
  environment: Environment;
  logLevel: LogLevel;
  port: Port;
}

const config: AppConfig = {
  environment: "dev",
  logLevel: "debug",
  port: 3000
};

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • typescript/第一章typescript基础.txt
  • 最后更改: 2026/03/04 16:42
  • 张叶安