likes
comments
collection
share

TypeScript 最佳实践:让你的代码不再“撞墙”

作者站长头像
站长
· 阅读数 16

今天和大家分享一些 TypeScript 最佳实践,这些实践不仅能让你的代码更加优雅,还能避免一些常见的“撞墙”问题。废话不多说,让我们开始吧!

使用 strictNullChecks

在 TypeScript 中,null 和 undefined 是默认可以赋值给任何类型的,这很容易导致一些潜在的 bug。使用 strictNullChecks 可以让 TypeScript 在编译时检查 null 和 undefined 的使用情况,避免这些问题的发生。

// 不使用 strictNullChecks
let foo: string = null; // 编译通过,运行时报错

// 使用 strictNullChecks
let bar: string | null = null; // 编译时报错,需要显式声明可能为 null 的变量类型

使用 readonly

在 TypeScript 中,可以使用 readonly 关键字将属性设置为只读,这样在编译时就能避免一些不必要的修改。

class Foo {
  public readonly bar: string = "hello";
}

const foo = new Foo();
foo.bar = "world"; // 编译时报错,无法修改 readonly 属性

使用 namespace

在 TypeScript 中,可以使用 namespace 将相关的代码组织在一起,避免命名冲突和全局变量污染。

namespace Foo {
  export function bar() {
    console.log("hello");
  }
}

Foo.bar(); // 输出 hello

使用 interface

在 TypeScript 中,可以使用 interface 定义对象的结构,这样可以提高代码的可读性和可维护性。

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

function greet(user: User) {
  console.log(`Hello, ${user.name}! You are ${user.age} years old.`);
}

const user = { name: "Alice", age: 18 };
greet(user); // 输出 Hello, Alice! You are 18 years old.

使用 type

在 TypeScript 中,可以使用 type 定义类型别名,这样可以提高代码的可读性和可维护性。

type User = {
  name: string;
  age: number;
};

function greet(user: User) {
  console.log(`Hello, ${user.name}! You are ${user.age} years old.`);
}

const user = { name: "Alice", age: 18 };
greet(user); // 输出 Hello, Alice! You are 18 years old.

使用泛型

在 TypeScript 中,可以使用泛型提高代码的灵活性和复用性。

function identity<T>(arg: T): T {
  return arg;
}

console.log(identity("hello")); // 输出 hello
console.log(identity(123)); // 输出 123

使用枚举

在 TypeScript 中,可以使用枚举提高代码的可读性和可维护性。

enum Color {
  Red,
  Green,
  Blue,
}

console.log(Color.Red); // 输出 0
console.log(Color.Green); // 输出 1
console.log(Color.Blue); // 输出 2

使用装饰器

在 TypeScript 中,可以使用装饰器扩展类和方法的功能。

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log(`[${new Date().toISOString()}] ${propertyKey}(${args})`);
    return originalMethod.apply(this, args);
  };
}

class Foo {
  @log
  bar() {
    console.log("hello");
  }
}

const foo = new Foo();
foo.bar(); // 输出 [2021-10-01T00:00:00.000Z] bar()
           //      hello

使用 Promise

在 TypeScript 中,可以使用 Promise 处理异步操作,避免回调地狱和错误处理问题。

function fetchData(): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("hello");
    }, 1000);
  });
}

async function main() {
  try {
    const data = await fetchData();
    console.log(data); // 输出 hello
  } catch (error) {
    console.error(error);
  }
}

main();

使用 async/await

在 TypeScript 中,可以使用 async/await 处理异步操作,使代码更加简洁和可读。

function fetchData(): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("hello");
    }, 1000);
  });
}

async function main() {
  const data = await fetchData();
  console.log(data); // 输出 hello
}

main();

使用模块化

在 TypeScript 中,可以使用模块化将代码组织起来,避免全局变量污染和命名冲突。

// greeter.ts
export function greet(name: string) {
  console.log(`Hello, ${name}!`);
}

// main.ts
import { greet } from "./greeter";

greet("Alice"); // 输出 Hello, Alice!

使用命名空间别名

在 TypeScript 中,可以使用命名空间别名提高代码的可读性和可维护性。

namespace Foo.Bar.Baz {
  export function qux() {}
}

import baz = Foo.Bar.Baz;
baz.qux();

使用类型断言

在 TypeScript 中,可以使用类型断言将一个值断言为某个类型,避免一些类型推导问题。

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

const user: any = { name: "Alice", age: "18" };
console.log((user as User).name); // 输出 Alice
console.log((user as User).age); // 输出 undefined(因为 age 是字符串类型)

使用非空断言

在 TypeScript 中,可以使用非空断言告诉编译器一个变量不会是 null 或 undefined。

let foo!: string;

console.log(foo.length); // 不会报错(因为 foo 已经被标记为非空)

使用 unknown

在 TypeScript 中,可以使用 unknown 表示一个未知类型的值,这样可以提高代码的健壮性和可维护性。

function foo(input: unknown) {
  if (typeof input === "string") {
    console.log(input.toUpperCase());
  } else if (Array.isArray(input)) {
    console.log(input.join(","));
  } else if (typeof input === "object" && input !== null) {
    console.log(Object.keys(input));
  } else {
    console.log("unknown");
  }
}

foo("hello"); // 输出 HELLO
foo([1,2,3]); // 输出 1,2,3
foo({ a: 1, b: 2 }); // 输出 a,b
foo(null); // 输出 unknown

使用 as const

在 TypeScript 中,可以使用 as const 将一个对象或数组中的所有值都标记为 readonly 和字面量类型。

const foo = {
  bar: "hello",
} as const;

foo.bar = "world"; // 编译时报错,无法修改 readonly 属性

const bar = [1,2,3] as const;

bar.push(4); // 编译时报错,无法修改数组长度

使用 keyof

在 TypeScript 中,可以使用 keyof 获取一个对象的所有键名。

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

type UserKey = keyof User;

const key: UserKey = "name"; // 编译通过
const key2: UserKey = "email"; // 编译时报错,email 不是 User 的键名之一

使用 in

在 TypeScript 中,可以使用 in 判断一个属性是否存在于一个对象中。

interface User {
  name?: string;
  age?: number;
}

function greet(user: User) {
  if ("name" in user) {
    console.log(`Hello, ${user.name}!`);
  } else if ("age" in user) {
    console.log(`You are ${user.age} years old.`);
  } else {
    console.log("Unknown user.");
  }
}

greet({ name: "Alice" }); // 输出 Hello, Alice!
greet({ age: 18 }); // 输出 You are 18 years old.
greet({}); // 输出 Unknown user.

使用 as unknown as T

在 TypeScript 中,可以使用 as unknown as T 将一个值断言为某个类型,并且避免类型检查错误。

const data = JSON.parse('{"name": "Alice", "age": "18"}');

console.log((data as unknown as User).name); // 输出 Alice(因为 age 是字符串类型)
console.log((data as unknown as User).age); // 输出 undefined(因为 age 是字符串类型)

使用 Partial

在 TypeScript 中,可以使用 Partial 将一个对象中所有属性都变成可选属性。

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

function updateUserInfo(user: Partial<User>) {}

updateUserInfo({}); // 编译通过(因为所有属性都是可选的)
updateUserInfo({ name: "Alice" }); // 编译通过(因为所有属性都是可选的)
updateUserInfo({ name: "Alice", age: 18 }); // 编译通过(因为所有属性都是可选的)

希望这些实践能够帮助大家写出更加优雅、健壮和可维护的代码。如果你有其他好的实践,欢迎留言分享给大家!

转载自:https://juejin.cn/post/7226930744769626169
评论
请登录