阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > TypeScript:让JavaScript喝上一杯严谨的茶

TypeScript:让JavaScript喝上一杯严谨的茶

作者:陈川 阅读数:16890人阅读 分类: 前端综合

TypeScript 就像给 JavaScript 泡了一杯严谨的茶,既保留了原有的灵活风味,又添了几分静态类型的醇厚。它让代码在运行前就能暴露潜在问题,同时提供了更智能的工具支持,像是给狂野的 JavaScript 系上了一条安全带。

TypeScript 的类型系统:从"随心所欲"到"约法三章"

JavaScript 开发者都熟悉这种场景:

javascript 复制代码
function add(a, b) {
  return a + b;
}
console.log(add(1, 2));     // 3
console.log(add('1', '2')); // '12'

同样的函数,输入数字返回求和,输入字符串却变成拼接。TypeScript 通过类型注解解决了这个问题:

typescript 复制代码
function add(a: number, b: number): number {
  return a + b;
}
add(1, 2);     // 正确
add('1', '2'); // 编译时报错

类型系统不仅限于基本类型,还能定义复杂结构:

typescript 复制代码
interface User {
  id: number;
  name: string;
  email?: string;  // 可选属性
}

function registerUser(user: User) {
  // 实现逻辑
}

registerUser({ id: 1, name: 'Alice' }); // 正确
registerUser({ name: 'Bob' }); // 错误:缺少必需的id属性

高级类型:类型系统的瑞士军刀

TypeScript 的真正威力在于其高级类型系统。比如这个处理API响应的例子:

typescript 复制代码
type ApiResponse<T> = {
  data: T;
  status: number;
  error?: string;
};

type UserProfile = {
  id: string;
  username: string;
  avatar: string;
};

async function fetchUserProfile(userId: string): Promise<ApiResponse<UserProfile>> {
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
}

// 使用时能获得完整的类型提示
const result = await fetchUserProfile('123');
if (result.error) {
  console.error(result.error);
} else {
  console.log(result.data.username); // 类型安全访问
}

联合类型和类型守卫让代码更健壮:

typescript 复制代码
type Shape = 
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; size: number }
  | { kind: 'rectangle'; width: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.size ** 2;
    case 'rectangle':
      return shape.width * shape.height;
    default:
      // TypeScript会确保所有情况都被处理
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

泛型:编写可复用的类型安全代码

泛型让组件既能保持灵活性又不失类型安全。看看这个React组件示例:

typescript 复制代码
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// 使用时
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

<UserList 
  items={users}
  renderItem={(user) => <span>{user.name}</span>} 
/>;
// 自动推断user类型为{ id: number; name: string }

装饰器:给代码加点"语法糖"

TypeScript 装饰器为类和类成员添加了声明式语法:

typescript 复制代码
function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  
  descriptor.value = function(...args: any[]) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();
    console.log(`${propertyKey} executed in ${(end - start).toFixed(2)}ms`);
    return result;
  };
  
  return descriptor;
}

class Calculator {
  @logExecutionTime
  static fibonacci(n: number): number {
    if (n <= 1) return n;
    return Calculator.fibonacci(n - 1) + Calculator.fibonacci(n - 2);
  }
}

Calculator.fibonacci(10); // 控制台会输出执行时间

类型推断与类型体操

TypeScript 的类型推断能力常常令人惊喜。看看这个条件类型示例:

typescript 复制代码
type IsString<T> = T extends string ? true : false;

type A = IsString<'hello'>;  // true
type B = IsString<123>;      // false

// 更实用的例子:提取Promise的返回值类型
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;

type C = UnpackPromise<Promise<string>>;  // string
type D = UnpackPromise<number>;           // number

映射类型可以批量转换接口属性:

typescript 复制代码
interface Person {
  name: string;
  age: number;
  email: string;
}

type ReadonlyPerson = Readonly<Person>;
// 等同于:
// {
//   readonly name: string;
//   readonly age: number;
//   readonly email: string;
// }

type PartialPerson = Partial<Person>;
// 等同于:
// {
//   name?: string;
//   age?: number;
//   email?: string;
// }

与现代JavaScript生态的无缝集成

TypeScript 对现代JavaScript特性的支持非常全面。比如这个使用可选链和空值合并的例子:

typescript 复制代码
interface Company {
  name: string;
  address?: {
    street?: string;
    city?: string;
  };
}

function getCompanyCity(company: Company): string {
  return company.address?.city ?? '未知城市';
}

const company1 = { name: 'Acme' };
console.log(getCompanyCity(company1)); // "未知城市"

const company2 = { 
  name: 'Globex', 
  address: { city: 'New York' } 
};
console.log(getCompanyCity(company2)); // "New York"

工程化优势:不只是类型检查

TypeScript 的工程化特性让大型项目维护更轻松。比如路径别名:

json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@components/*": ["components/*"],
      "@utils/*": ["utils/*"]
    }
  }
}

然后在代码中可以这样使用:

typescript 复制代码
import { Button } from '@components/Button';
import { formatDate } from '@utils/date';

项目引用(Project References)让monorepo管理更轻松:

json 复制代码
// tsconfig.json
{
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/ui" }
  ]
}

类型声明文件:与JavaScript世界和平共处

即使不使用TypeScript编写的库,也能通过声明文件获得类型支持。比如为旧版jQuery添加类型:

typescript 复制代码
// @types/jquery/index.d.ts
declare interface JQuery {
  modal(action: 'show' | 'hide'): void;
  carousel(options?: any): void;
}

declare var $: {
  (selector: string): JQuery;
  ajax(url: string, settings?: any): Promise<any>;
};

对于现代npm包,可以直接安装@types包:

bash 复制代码
npm install --save-dev @types/lodash

然后就能享受完整的类型提示:

typescript 复制代码
import { debounce, throttle } from 'lodash';

const debouncedFn = debounce(() => {
  console.log('Resized!');
}, 200);

window.addEventListener('resize', debouncedFn);

从JavaScript迁移的渐进式路径

TypeScript 允许逐步迁移现有JavaScript项目。可以从重命名文件开始:

复制代码
//  .js 文件改为 .ts  .tsx
// 先设置 allowJs: true  checkJs: false

然后逐步添加类型注解。对于复杂的现有代码,可以使用any类型暂时绕过类型检查:

typescript 复制代码
// 迁移初期可以这样
const legacyData: any = getLegacyData();
// 后期再逐步替换为具体类型

JSDoc注释也能被TypeScript识别,实现渐进增强:

javascript 复制代码
/**
 * @param {number} a
 * @param {number} b
 * @returns {number}
 */
function add(a, b) {
  return a + b;
}

TypeScript的未来:持续演进的类型系统

每个TypeScript版本都会带来新特性。比如4.9引入的satisfies操作符:

typescript 复制代码
const colors = {
  red: '#ff0000',
  green: '#00ff00',
  blue: '#0000ff'
} satisfies Record<string, `#${string}`>;

// 既验证了值格式,又保留了字面量类型

5.0引入的装饰器新标准:

typescript 复制代码
@registerComponent
class MyComponent {
  @observable
  count = 0;

  @action
  increment() {
    this.count++;
  }
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌