TypeScript 基础入门:从 JavaScript 到类型安全的演进

TypeScript 基础入门:从 JavaScript 到类型安全的演进

【免费下载链接】TypeScript TypeScript 使用手册(中文版)翻译。http://www.typescriptlang.org 【免费下载链接】TypeScript 项目地址: https://gitcode.com/gh_mirrors/typ/TypeScript

引言:为什么需要 TypeScript?

你是否曾经在 JavaScript 开发中遇到过这样的问题:

  • 深夜调试时发现变量名拼写错误,导致整个功能失效
  • 函数参数类型不匹配,运行时才报错
  • 重构代码时不确定哪些地方使用了某个接口
  • 团队协作时接口定义不清晰,沟通成本高

TypeScript 正是为了解决这些问题而生!作为 JavaScript 的超集(Superset),TypeScript 在保留 JavaScript 所有特性的基础上,添加了强大的静态类型系统,让开发者在编码阶段就能发现潜在错误,大大提升代码质量和开发效率。

通过本文,你将掌握:

  • TypeScript 的核心概念和基础语法
  • 类型系统的工作原理和最佳实践
  • 如何从 JavaScript 平滑过渡到 TypeScript
  • 实际项目中的类型安全编程技巧

TypeScript 概述

什么是 TypeScript?

TypeScript 是由 Microsoft 开发的开源编程语言,它扩展了 JavaScript 的语法,添加了可选的静态类型和基于类的面向对象编程。TypeScript 设计目标包括:

  • 类型安全:在编译时捕获类型错误
  • 更好的工具支持:智能提示、代码重构、导航
  • 渐进式采用:可以逐步将 JavaScript 项目迁移到 TypeScript
  • ECMAScript 兼容:支持最新的 JavaScript 特性

TypeScript 与 JavaScript 的关系

mermaid

TypeScript 不是要取代 JavaScript,而是增强它。所有有效的 JavaScript 代码都是有效的 TypeScript 代码。

基础类型系统

原始数据类型

TypeScript 支持 JavaScript 的所有原始数据类型,并提供了明确的类型注解:

// 布尔值
let isDone: boolean = false;

// 数字:支持十进制、十六进制、二进制、八进制
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

// 字符串:支持单引号、双引号、模板字符串
let color: string = "blue";
let fullName: string = `Bob Smith`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}. I'll be ${age + 1} years old next month.`;

// 空值:void 表示没有任何类型
function warnUser(): void {
    console.log("This is my warning message");
}

// Null 和 Undefined
let u: undefined = undefined;
let n: null = null;

数组和元组

// 数组:两种声明方式
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];

// 元组:固定类型和长度的数组
let tuple: [string, number];
tuple = ["hello", 10]; // OK
tuple = [10, "hello"]; // Error: 类型不匹配

枚举类型

枚举(Enum)是 TypeScript 特有的数据类型,用于定义命名常量集合:

enum Color {
    Red = 1,    // 可以手动指定值
    Green,      // 自动递增为 2
    Blue = 4    // 也可以跳过
}

let c: Color = Color.Green;
console.log(c); // 输出: 2
console.log(Color[2]); // 输出: "Green"

Any 和 Unknown 类型

特性any 类型unknown 类型
任意赋值✅ 允许✅ 允许
任意方法调用✅ 允许❌ 不允许
类型安全❌ 不安全✅ 相对安全
需要类型检查❌ 不需要✅ 需要
// any 类型:完全放弃类型检查
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // OK
notSure.ifItExists(); // OK(运行时可能出错)

// unknown 类型:需要类型检查后才能使用
let uncertain: unknown = 4;
uncertain = "maybe a string instead";

// 需要类型检查
if (typeof uncertain === "string") {
    console.log(uncertain.toUpperCase()); // OK
}

函数类型系统

函数类型注解

TypeScript 为函数提供了完整的类型支持:

// 函数声明
function add(x: number, y: number): number {
    return x + y;
}

// 函数表达式
let myAdd = function(x: number, y: number): number { 
    return x + y; 
};

// 完整函数类型
let myAdd2: (x: number, y: number) => number = function(x, y) { 
    return x + y; 
};

可选参数和默认参数

// 可选参数
function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + " " + lastName;
    } else {
        return firstName;
    }
}

// 默认参数
function buildName2(firstName: string, lastName = "Smith") {
    return firstName + " " + lastName;
}

// 剩余参数
function buildName3(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}

函数重载

TypeScript 支持函数重载,让一个函数能够处理多种参数类型:

// 重载签名
function pickCard(x: {suit: string; card: number}[]): number;
function pickCard(x: number): {suit: string; card: number};

// 实现签名
function pickCard(x: any): any {
    if (typeof x == "object") {
        return Math.floor(Math.random() * x.length);
    } else if (typeof x == "number") {
        return { suit: "hearts", card: x % 13 };
    }
}

接口(Interface)

接口基础

接口是 TypeScript 的核心概念,用于定义对象的形状:

interface LabelledValue {
    label: string;
    size?: number;      // 可选属性
    readonly x: number; // 只读属性
}

function printLabel(labelledObj: LabelledValue) {
    console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object", x: 5};
printLabel(myObj);

函数类型接口

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, sub: string) {
    return source.search(sub) > -1;
};

可索引类型接口

interface StringArray {
    [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];

类类型接口

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

class Clock implements ClockInterface {
    currentTime: Date = new Date();
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) {}
}

类(Class)

类的基本用法

TypeScript 提供了完整的面向对象编程支持:

class Animal {
    // 属性
    name: string;
    
    // 构造函数
    constructor(theName: string) { 
        this.name = theName; 
    }
    
    // 方法
    move(distanceInMeters: number = 0) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

class Snake extends Animal {
    constructor(name: string) { 
        super(name); 
    }
    
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

let sam = new Snake("Sammy the Python");
sam.move();

访问修饰符

TypeScript 支持三种访问修饰符:

修饰符类内部子类实例
public✅ 可访问✅ 可访问✅ 可访问
protected✅ 可访问✅ 可访问❌ 不可访问
private✅ 可访问❌ 不可访问❌ 不可访问
class Person {
    public name: string;          // 公共属性
    protected age: number;        // 受保护属性
    private secret: string;       // 私有属性
    
    constructor(name: string, age: number, secret: string) {
        this.name = name;
        this.age = age;
        this.secret = secret;
    }
    
    public getInfo(): string {
        return `${this.name} is ${this.age} years old`;
    }
}

抽象类

abstract class Department {
    constructor(public name: string) {}
    
    abstract printMeeting(): void; // 必须在派生类中实现
    
    printName(): void {
        console.log("Department name: " + this.name);
    }
}

class AccountingDepartment extends Department {
    constructor() {
        super("Accounting and Auditing");
    }
    
    printMeeting(): void {
        console.log("The Accounting Department meets each Monday at 10am.");
    }
}

泛型(Generics)

泛型基础

泛型提供了代码重用的强大机制:

// 泛型函数
function identity<T>(arg: T): T {
    return arg;
}

let output1 = identity<string>("myString"); // 显式指定类型
let output2 = identity("myString");         // 类型推断

// 泛型接口
interface GenericIdentityFn<T> {
    (arg: T): T;
}

let myIdentity: GenericIdentityFn<number> = identity;

泛型约束

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length); // 现在知道 arg 有 length 属性
    return arg;
}

loggingIdentity({length: 10, value: 3}); // OK
loggingIdentity(3); // Error: number 没有 length 属性

泛型类

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

类型操作和高级特性

联合类型和交叉类型

// 联合类型:可以是多种类型之一
let padding: string | number;

// 交叉类型:同时具有多种类型的特性
interface BusinessPartner {
    name: string;
    credit: number;
}

interface Identity {
    id: number;
    name: string;
}

type Employee = Identity & BusinessPartner;

let e: Employee = {
    id: 1, 
    name: "John",
    credit: 1000
};

类型别名和字面量类型

// 类型别名
type StringOrNumber = string | number;

// 字面量类型
type Easing = "ease-in" | "ease-out" | "ease-in-out";
type DiceValue = 1 | 2 | 3 | 4 | 5 | 6;

function animate(dx: number, dy: number, easing: Easing) {
    if (easing === "ease-in") {
        // ...
    }
}

类型断言

类型断言告诉编译器"相信我,我知道这个值的类型":

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

// as 语法(推荐,特别是在 JSX 中)
let strLength2: number = (someValue as string).length;

从 JavaScript 迁移到 TypeScript

迁移策略

mermaid

步骤详解

  1. 安装 TypeScript

    npm install -g typescript
    
  2. 创建 tsconfig.json

    {
      "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "strict": false,
        "esModuleInterop": true
      }
    }
    
  3. 逐步迁移文件

    • .js 文件重命名为 .ts
    • 先使用 any 类型,然后逐步细化
    • 启用越来越严格的编译选项
  4. 处理第三方库

    • 使用 @types 包提供类型定义
    • 对于没有类型定义的库,可以创建声明文件

常见问题解决

// 1. 处理没有类型定义的第三方库
declare module "some-untyped-library";

// 2. 处理动态属性访问
interface DynamicObject {
    [key: string]: any;
}

// 3. 处理混合类型
type MixedType = string | number | boolean;

实战示例:构建类型安全的表单验证

让我们通过一个实际例子来展示 TypeScript 的价值:

// 定义表单数据类型
interface UserFormData {
    username: string;
    email: string;
    age?: number;
    preferences: string[];
}

// 验证函数
function validateForm(data: UserFormData): { isValid: boolean; errors: string[] } {
    const errors: string[] = [];
    
    // 用户名验证
    if (!data.username || data.username.length < 3) {
        errors.push("用户名至少需要3个字符");
    }
    
    // 邮箱验证
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!data.email || !emailRegex.test(data.email)) {
        errors.push("请输入有效的邮箱地址");
    }
    
    // 年龄验证(可选)
    if (data.age !== undefined && (data.age < 0 || data.age > 150)) {
        errors.push("年龄必须在0-150之间");
    }
    
    return {
        isValid: errors.length === 0,
        errors
    };
}

// 使用示例
const formData: UserFormData = {
    username: "john_doe",
    email: "john@example.com",
    preferences: ["typescript", "programming"]
};

const result = validateForm(formData);
if (!result.isValid) {
    console.error("表单验证失败:", result.errors);
} else {
    console.log("表单验证通过");
}

TypeScript 最佳实践

代码组织建议

  1. 合理使用接口和类型别名

    • 接口用于对象形状,支持扩展
    • 类型别名用于联合类型、元组等复杂类型
  2. 避免过度使用 any

    • 尽量使用具体的类型
    • 使用 unknown 代替 any 提高安全性
  3. 利用类型推断

    • 让 TypeScript 自动推断简单类型
    • 只在必要时显式注解类型

性能优化技巧

// 使用 const 枚举减少运行时开销
const enum Direction {
    Up,
    Down,
    Left,
    Right
}

// 使用 readonly 数组防止意外修改
function processItems(items: readonly string[]) {
    // items.push("new"); // Error: push 不存在于 readonly 数组
}

团队协作规范

// 使用命名空间组织代码
namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
    
    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && /^\d+$/.test(s);
        }
    }
}

// 使用模块化导出
export { Validation };

总结与展望

TypeScript 不仅仅是一个类型系统,它是一个完整的开发体验提升工具。通过本文的学习,你应该已经掌握了:

  • ✅ TypeScript 的基础类型系统和语法
  • ✅ 接口、类、泛型等高级特性
  • ✅ 从 JavaScript 迁移到 TypeScript 的策略
  • ✅ 实际项目中的最佳实践

TypeScript 的生态系统正在快速发展,越来越多的库和框架都提供了完整的类型支持。无论是前端开发(React、Vue、Angular)还是后端开发(Node.js),TypeScript 都能提供更好的开发体验和代码质量。

下一步学习建议

  1. 深入学习高级类型:条件类型、映射类型、模板字面量类型
  2. 掌握工程配置:tsconfig.json 的各种选项和优化
  3. 了解声明文件:如何为第三方库编写类型定义
  4. 实践大型项目:模块化、命名空间、项目引用

开始你的 TypeScript 之旅吧!类型安全的世界正在等待你的探索。


延伸阅读提示:本文只是 TypeScript 的入门介绍,更多高级特性和最佳实践请参考官方文档和社区资源。记住,TypeScript 的学习是一个渐进的过程,不要试图一次性掌握所有特性,而是应该在实践中逐步深入。

【免费下载链接】TypeScript TypeScript 使用手册(中文版)翻译。http://www.typescriptlang.org 【免费下载链接】TypeScript 项目地址: https://gitcode.com/gh_mirrors/typ/TypeScript

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

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值