编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

JavaScript对比TypeScript(JavaScript对比两个json)

wxchong 2025-03-13 21:14:16 开源技术 14 ℃ 0 评论

JavaScript对比TypeScript

JavaScript对比TypeScript

作为一名 JavaScript 工程师,我经常被问到:"为什么要使用 TypeScript?"或者"TypeScript 相比 JavaScript 有什么优势?"今天,让我们通过实际的代码示例来深入探讨这个话题。

核心特性对比

1. 类型系统:最显著的区别

// JavaScript
function calculateTotal(items) {
  return items.reduce((total, item) => total + item.price, 0);
}

// 这段代码可能在运行时出错
const items = [
  { price: 10 },
  { price: 20 },
  { notPrice: 30 }  // 这里会导致运行时错误
];

console.log(calculateTotal(items)); // 运行时才发现 undefined + number 的错误
// TypeScript
interface Item {
  price: number;
}

function calculateTotal(items: Item[]): number {
  return items.reduce((total, item) => total + item.price, 0);
}

// 在编译时就会发现错误
const items = [
  { price: 10 },
  { price: 20 },
  { notPrice: 30 }  // TypeScript 会在编译时报错!
];

2. 接口和类型定义

// JavaScript
const user = {
  name: 'John',
  age: 30,
  address: {
    street: '123 Main St',
    city: 'Boston'
  }
};

// 没有明确的契约,可能导致使用时的不确定性
function updateUser(user) {
  // 这里我们并不确定 user 对象应该包含哪些属性
  user.name = 'Jane';
}
// TypeScript
interface Address {
  street: string;
  city: string;
  zipCode?: string; // 可选属性
}

interface User {
  name: string;
  age: number;
  address: Address;
}

function updateUser(user: User): void {
  user.name = 'Jane'; // IDE 会提供完整的属性提示
}

3. 函数重载

// JavaScript
function process(input) {
  if (typeof input === 'string') {
    return input.toUpperCase();
  } else if (Array.isArray(input)) {
    return input.map(item => item.toUpperCase());
  }
  throw new Error('Unsupported input type');
}
// TypeScript
function process(input: string): string;
function process(input: string[]): string[];
function process(input: string | string[]): string | string[] {
  if (typeof input === 'string') {
    return input.toUpperCase();
  } else {
    return input.map(item => item.toUpperCase());
  }
}

4. 泛型

// JavaScript
function firstElement(arr) {
  return arr[0];
}

// 无法在编译时确保类型安全
const numResult = firstElement([1, 2, 3]);
const strResult = firstElement(['a', 'b', 'c']);
// TypeScript
function firstElement(arr: T[]): T | undefined {
  return arr[0];
}

// 类型安全且具有更好的 IDE 支持
const numResult = firstElement([1, 2, 3]); // 类型为 number
const strResult = firstElement(['a', 'b', 'c']); // 类型为 string

TypeScript 特有的语法特性

1. 类型注解(Type Annotations)

// JavaScript
let name = "John";
let age = 30;
let isStudent = true;
let numbers = [1, 2, 3];
// JavaScript 中没有元组类型,使用普通数组
let tuple = ["hello", 10];

// JavaScript 中可以使用 JSDoc 注释来提供类型信息
/**
 * @param {string} name
 * @returns {string}
 */
function greet(name) {
    return `Hello, ${name}!`;
}
// TypeScript
let name: string = "John";
let age: number = 30;
let isStudent: boolean = true;
let numbers: number[] = [1, 2, 3];
let tuple: [string, number] = ["hello", 10];

// 函数参数和返回值的类型注解
function greet(name: string): string {
    return `Hello, ${name}!`;
}

2. 枚举(Enums)

// JavaScript 实现方式 1:使用对象
const Direction = {
    Up: "UP",
    Down: "DOWN",
    Left: "LEFT",
    Right: "RIGHT",
    // 防止枚举值被修改
    Object.freeze(Direction);
};

// JavaScript 实现方式 2:使用 Symbol
const Direction = {
    Up: Symbol("UP"),
    Down: Symbol("DOWN"),
    Left: Symbol("LEFT"),
    Right: Symbol("RIGHT")
};

let playerDirection = Direction.Up;

// 数字枚举的实现
const StatusCode = {
    OK: 200,
    NotFound: 404,
    Error: 500,
    Object.freeze(StatusCode);
};
// TypeScript
enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT"
}

let playerDirection: Direction = Direction.Up;

// 数字枚举
enum StatusCode {
    OK = 200,
    NotFound = 404,
    Error = 500
}

3. 类型断言(Type Assertions)

// JavaScript
let someValue = "this is a string";
let strLength = someValue.length;

// JavaScript 中使用类型检查
if (typeof someValue === 'string') {
    let strLength = someValue.length;
}

// DOM 操作
const myCanvas = document.getElementById('main_canvas');
if (myCanvas instanceof HTMLCanvasElement) {
    // 使用 canvas API
    const ctx = myCanvas.getContext('2d');
}
// TypeScript
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
// 或者
let strLength: number = (someValue).length;

// DOM 操作的类型断言
const myCanvas = document.getElementById('main_canvas') as HTMLCanvasElement;

4. 访问修饰符

// JavaScript
class Employee {
    #name; // 私有字段(ES2019+)
    _age; // 约定俗成的protected字段
    department;

    constructor(name, age, department, id) {
        this.#name = name;
        this._age = age;
        this.department = department;
        Object.defineProperty(this, 'id', {
            value: id,
            writable: false // 实现 readonly
        });
    }

    #getDetails() { // 私有方法(ES2019+)
        return `${this.#name} (${this._age})`;
    }
}
// TypeScript
class Employee {
    private name: string;
    protected age: number;
    public department: string;
    readonly id: number;

    constructor(name: string, age: number, department: string, id: number) {
        this.name = name;
        this.age = age;
        this.department = department;
        this.id = id;
    }

    private getDetails(): string {
        return `${this.name} (${this.age})`;
    }
}

5. 抽象类和接口

// JavaScript
// 模拟抽象类
class Animal {
    constructor() {
        if (new.target === Animal) {
            throw new Error('Animal is abstract');
        }
    }

    makeSound() {
        throw new Error('makeSound must be implemented');
    }

    move() {
        console.log("Moving...");
    }
}

// 模拟接口
class Pet {
    constructor() {
        if (this.play === undefined) {
            throw new Error('Must implement play method');
        }
        if (!this.name) {
            throw new Error('Must have name property');
        }
    }
}

class Dog extends Animal {
    constructor(name) {
        super();
        this.name = name;
    }

    makeSound() {
        console.log("Woof!");
    }

    play() {
        console.log("Playing fetch!");
    }
}
// TypeScript
abstract class Animal {
    abstract makeSound(): void;
    move(): void {
        console.log("Moving...");
    }
}

interface Pet {
    name: string;
    play(): void;
}

class Dog extends Animal implements Pet {
    name: string;
    
    constructor(name: string) {
        super();
        this.name = name;
    }

    makeSound(): void {
        console.log("Woof!");
    }

    play(): void {
        console.log("Playing fetch!");
    }
}

6. 联合类型和交叉类型

// JavaScript
function processValue(value) {
    if (typeof value === "string") {
        return value.toUpperCase();
    }
    if (typeof value === "number") {
        return value * 2;
    }
    throw new Error('Invalid type');
}

// 对象合并
const person = {
    ...{ name: "John" },
    ...{ age: 30 }
};
// TypeScript
type StringOrNumber = string | number;
type NameAndAge = { name: string } & { age: number };

function processValue(value: StringOrNumber) {
    if (typeof value === "string") {
        return value.toUpperCase();
    }
    return value * 2;
}

const person: NameAndAge = {
    name: "John",
    age: 30
};

7. 可选链和空值合并

// JavaScript ES2020+
const user = {
    name: "John"
};

// 可选链
const city = user.address?.city;

// 空值合并
const street = user.address?.street ?? "Default Street";

// 旧版 JavaScript
const city = user && user.address && user.address.city;
const street = (user && user.address && user.address.street) || "Default Street";
// TypeScript(也适用于现代 JavaScript)
interface User {
    name: string;
    address?: {
        street?: string;
        city?: string;
    };
}

const user: User = {
    name: "John"
};

// 可选链
const city = user.address?.city;

// 空值合并
const street = user.address?.street ?? "Default Street";

8. 字面量类型

// JavaScript
const CARDINAL_DIRECTIONS = ["North", "South", "East", "West"];
const DICE_VALUES = [1, 2, 3, 4, 5, 6];

function move(direction) {
    if (!CARDINAL_DIRECTIONS.includes(direction)) {
        throw new Error('Invalid direction');
    }
    console.log(`Moving ${direction}`);
}

move("North"); // 有效
move("Northeast"); // 运行时错误
// TypeScript
type CardinalDirection = "North" | "South" | "East" | "West";
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

function move(direction: CardinalDirection) {
    console.log(`Moving ${direction}`);
}

move("North"); // 有效
// move("Northeast"); // 编译错误

9. 类型别名和映射类型

// JavaScript
const createPoint = (x, y) => ({
    x,
    y
});

// 创建只读对象
const createReadonlyPoint = (x, y) => 
    Object.freeze({
        x,
        y
    });

const point = createReadonlyPoint(10, 20);
// point.x = 30; // 错误:Cannot assign to read only property 'x'
// TypeScript
type Point = {
    x: number;
    y: number;
};

type Readonly = {
    readonly [P in keyof T]: T[P];
};

type ReadonlyPoint = Readonly;

const point: ReadonlyPoint = { x: 10, y: 20 };
// point.x = 30; // 错误:无法分配到 "x" ,因为它是只读属性

10. 装饰器(Decorators)

// JavaScript(使用实验性的装饰器语法)
function log(target, propertyKey) {
    console.log(`Accessing property: ${propertyKey}`);
}

class Example {
    @log
    name = "example";
}

// JavaScript(不使用装饰器的替代方案)
function makeLoggable(target) {
    const originalDescriptor = Object.getOwnPropertyDescriptor(target.prototype, 'name');
    Object.defineProperty(target.prototype, 'name', {
        get() {
            console.log('Accessing property: name');
            return originalDescriptor.get.call(this);
        },
        set(value) {
            console.log('Setting property: name');
            originalDescriptor.set.call(this, value);
        }
    });
    return target;
}

@makeLoggable
class Example {
    name = "example";
}
// TypeScript
function log(target: any, propertyKey: string) {
    console.log(`Accessing property: ${propertyKey}`);
}

class Example {
    @log
    name: string = "example";
}

这些语法特性使得 TypeScript 能够:

  1. 提供更强大的类型检查和编译时验证
  2. 支持面向对象编程的高级特性
  3. 提供更好的代码组织和重用机制
  4. 增强代码的可读性和可维护性
  5. 提供更好的 IDE 支持和开发体验

虽然很多特性在现代 JavaScript 中也可以实现,但实现方式往往更复杂,且缺少编译时的类型检查。TypeScript 的优势在于它提供了更简洁、更安全的语法,以及强大的类型系统支持。

如何在项目中使用 TypeScript

1. 初始化项目

# 创建新项目
mkdir my-ts-project
cd my-ts-project
npm init -y

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

# 初始化 TypeScript 配置
npx tsc --init

2. 配置 tsconfig.json

{
  "compilerOptions": {
    "target": "es2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

3. 项目结构

my-ts-project/
├── src/
│   └── index.ts
├── package.json
├── tsconfig.json
└── node_modules/

4. 开发工作流

  1. 编写 TypeScript 代码(.ts 文件)
  2. 使用 tsc 编译代码:npx tsc
  3. 运行编译后的 JavaScript 代码:node dist/index.js

5. 推荐的开发工具

  • VS Code:内置 TypeScript 支持
  • ESLint 配置:
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint

总结

TypeScript 相比 JavaScript 的主要优势:

  1. 静态类型检查,提前发现潜在错误
  2. 更好的 IDE 支持,包括代码补全和重构
  3. 接口和类型定义提供更清晰的代码契约
  4. 更容易维护大型项目
  5. 通过类型推断减少文档需求

虽然需要一些学习成本,但 TypeScript 带来的好处远超过这些成本,特别是在大型项目中。作为一个 JavaScript 工程师,掌握 TypeScript 将显著提升你的开发效率和代码质量。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表