likes
comments
collection
share

TypeScript:让你的代码不再像个“any”了!

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

开篇

你是否曾经因为代码中的“any”而感到无从下手? 你是否曾经因为类型错误而烦恼不已? 别担心,今天我们就来谈谈如何用 TypeScript 打造一个无敌的前端应用!

TypeScript 是一门开源的编程语言,它是 JavaScript 的一个超集,支持其所有的语法和特性,并且在此基础上添加了一些新的语法和特性,使得编写和维护大型应用程序更加简单和可靠。本文将介绍 TypeScript 的常用语法和高阶语法,包括类型、面向对象编程、泛型、元组、枚举、接口、命名空间、模块化以及其他一些有用的特性。

类型

类型是 TypeScript 最重要的一个特性之一,它基于静态类型检查,可以在编译时发现代码中的类型错误,从而减少在运行时出现的错误。以下是 TypeScript 支持的各种类型以及它们的使用方法:

原始数据类型

TypeScript 中,原始数据类型共有 5 种,分别是 booleannumberstringnull 和 undefined。它们分别表示布尔值、数值、字符串、空值和未定义值。例如:

let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
let n: null = null;
let u: undefined = undefined;

数组

TypeScript 中,可以使用数组来表示一个元素的集合。数组可以是一维的、二维的甚至是多维的,每个元素的类型可以是任意的。例如:

let list1: number[] = [1, 2, 3];
let list2: Array<string> = ["a", "b", "c"]; // 数组泛型: Array<元素类型>:
let list3: boolean[][] = [[true, false], [false, true]];

元组

元组是一种特殊的数组类型,在元组中,每个元素可以拥有不同的类型。例如:

let tuple: [string, number, boolean] = ["hello", 10, true];

枚举

枚举是一种类型,它表示具有一组命名的常量的集合。例如:

enum Color {
  Red,
  Green,
  Blue,
}

let c: Color = Color.Green;
console.log(c); // 输出 1

Any

Any 类型表示任意类型,当不确定一个变量的类型时,可以使用 Any 类型。例如:

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;

Void

Void 类型表示没有任何类型,通常用于函数返回值,表示函数没有返回值。例如:

function warnUser(): void {
  console.log("This is a warning message");
}

 Never

Never 类型表示那些永远不会发生的结果,例如抛出异常或死循环。例如:

function error(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

Object

Object 类型表示非原始类型,也就是除了数值、字符串、布尔值、null、undefined 以外的类型。例如:

let obj: object = { foo: "bar" };

面向对象编程

TypeScript 支持面向对象编程的特性,包括类、接口、继承、多态等等。

类是一种包含属性和方法的结构体,它可以用来创建对象。例如:

class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  move(distance: number = 0) {
    console.log(`${this.name} moved ${distance} meters.`);
  }
}

let animal = new Animal("dog");
animal.move(10); // 输出 "dog moved 10 meters."

继承

继承是一种创建新类的重要机制,它允许一个类从另一个类中继承属性和方法。例如:

class Snake extends Animal {
  constructor(name: string) {
    super(name);
  }

  move(distance: number = 5) {
    console.log("Slithering");
    super.move(distance);
  }
}

let sam = new Snake("Sammy the Python");
sam.move(); // 输出 "Slithering" 和 "Sammy the Python moved 5 meters."

抽象类

抽象类是一种不能被实例化的类,它可以包含抽象方法和非抽象方法。抽象方法是一种必须由派生类实现的方法。例如:

abstract class Department {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  printName(): void {
    console.log("Department name: " + this.name);
  }

  abstract printMeeting(): void;
}

class AccountingDepartment extends Department {
  constructor() {
    super("Accounting and Auditing");
  }

  printMeeting(): void {
    console.log("The Accounting Department meets each Monday at 10am.");
  }

  generateReports(): void {
    console.log("Generating accounting reports...");
  }
}

let department: Department;
department = new AccountingDepartment();
department.printName();
department.printMeeting();

接口

接口是一种用于定义对象的形状和行为的结构体,它不会在编译后产生任何代码。例如:

interface Person {
  firstName: string;
  lastName: string;
}

function greeter(person: Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };
console.log(greeter(user)); // 输出 "Hello, Jane User"

泛型

泛型是一种编写通用代码的方法,它允许我们在编写代码时不预先指定具体的类型,在使用时再进行指定。例如:

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

let output = identity<string>("myString"); // 类型推断会自动推导出 output 的类型为 string

多态

多态是一种允许不同类的对象对同一消息做出不同的响应的机制。它可以通过引入抽象类和接口来实现。例如:

class Animal {
  sound(): void {
    console.log("Animal is making a sound");
  }
}

class Dog extends Animal {
  sound(): void {
    console.log("Dog is barking");
  }
}

class Cat extends Animal {
  sound(): void {
    console.log("Cat is meowing");
  }
}

function makeSound(animal: Animal) {
  animal.sound();
}

let dog = new Dog();
let cat = new Cat();
makeSound(dog); // 输出 "Dog is barking"
makeSound(cat); // 输出 "Cat is meowing"

其他语法

命名空间

命名空间是一种逻辑上的组织,可以用来解决不同模块之间的名称冲突。例如:

namespace MyNamespace {
  export interface MyInterface {
    name: string;
  }

  export class MyClass {
    sayHello(): void {
      console.log("Hello, world");
    }
  }
}

let myObject: MyNamespace.MyClass = new MyNamespace.MyClass();

模块化

模块化是一种将代码分成多个独立的文件的方式,每个文件包含不同的模块,并且通过导入和导出来实现模块之间的交互。例如:

// moduleName.ts
export function sayHello(name: string): void {
  console.log("Hello, " + name);
}

// app.ts
import { sayHello } from "./moduleName";
sayHello("world"); // 输出 "Hello, world"

类型别名

类型别名是一种为类型定义一个新的名字的方法。它可以用于创建复杂类型的别名,从而减少代码的重复。例如:

type MyString = string;
type MyObject = { name: string; age: number };
type CallbackFunction = (err: Error | null, result: any) => void;
type FirstName = string;
type LastName = string;
type FullName = `${FirstName} ${LastName}`;

function greeter(name: FullName) {
  return `Hello, ${name}!`;
}

let user = { firstName: "John", lastName: "Doe" };
console.log(greeter(`${user.firstName} ${user.lastName}`)); // 输出 "Hello, John Doe!"

字符串模板

字符串模板是一种用反引号 来括起来的字符串,它可以在其中使用插值表达式${}` 来插入变量或表达式。 例如:

let name = "world";
console.log(`Hello, ${name}`); // 输出 "Hello, world"

可选链操作符

可选链操作符 ?. 是一种用于检查某个对象属性是否存在的语法。如果属性不存在,它会返回 undefined 而不是抛出错误。例如:

let data = { name: "John", age: 20 };
console.log(data.address?.street?.name); // 输出 undefined,不会报错

类型推导

TypeScript 能够自动推导出变量的类型,这使得代码更加简洁和易于阅读。例如:

let x = 3; // TypeScript 推导出 x 的类型为 number

类型断言

类型断言是一种告诉 TypeScript 变量的类型的方式,通常用于处理一些类型不明确的情况。例如:

let str: any = "hello";
let len = (<string>str).length; // 使用类型断言告诉 TypeScript 变量 str 的类型为 string

合并接口

TypeScript 允许合并多个接口,从而创建出一个新的接口,这使得代码更加灵活和易于扩展。例如:

interface Shape {
  color: string;
}

interface Shape {
  width: number;
}

let shape: Shape = { color: "red", width: 100 };

可选属性和只读属性

TypeScript 允许给接口中的属性设置可选属性和只读属性,它们使得接口更加灵活和安全。例如:

interface Person {
  readonly name: string;
  age?: number;
}

let person: Person = { name: "John" };
person.age = 30;
person.name = "Jane"; // 报错,'name' 属性是只读的

索引签名

索引签名允许我们定义一个可以接收任意属性的接口,从而使得代码更加灵活和可扩展。例如:

interface Dictionary {
  [key: string]: string;
}

let dict: Dictionary = {
  foo: "bar",
  baz: "qux",
};

类型守卫

类型守卫是一种在运行时判断变量类型的机制,通常用于处理联合类型的场景。例如:

interface Bird {
  fly(): void;
}

interface Fish {
  swim(): void;
}

function isBird(arg: any): arg is Bird {
  return arg.fly !== undefined;
}

function makeSound(animal: Bird | Fish) {
  if (isBird(animal)) {
    animal.fly();
  } else {
    animal.swim();
  }
}

类型别名和联合类型

类型别名可以和联合类型一起使用,从而实现更加复杂的类型表示。例如:

type StringOrNumber = string | number;

function getValue(key: string): StringOrNumber | boolean {
  if (key === "x") {
    return 1;
  } else if (key === "y") {
    return "hello";
  } else {
    return false;
  }
}

let value = getValue("x");
if (typeof value === "string" || typeof value === "number") {
  console.log(value.toFixed(2));
} else {
  console.log(value);
}

类型推断和上下文类型

TypeScript 能够根据上下文推断出变量的类型,从而使得代码更加简洁和易于阅读。例如:

let values = [1, 2, 3, 4, 5];
let oddValues = values.filter((value) => value % 2 === 1);
console.log(oddValues); // 输出 "[1, 3, 5]"

在这个例子中,TypeScript 能够推断出 oddValues 的类型为 number[],因为它是由 values.filter 返回的结果推断出来的。

泛型和约束

泛型是一种允许代码重用、提高类型安全和灵活性的机制,它基于 TypeScript 的类型系统,可以实现可重用的类型表示和操作。例如:

interface Stack<T> {
  push(item: T): void;
  pop(): T | undefined;
}

class MyStack<T> implements Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }
}

let stack = new MyStack<string>();
stack.push("hello");
stack.push("world");
console.log(stack.pop()); // 输出 "world"

在这个例子中,泛型类型 T 可以表示任何类型,它作为类 MyStack 和接口 Stack 的类型参数,使得 Stack 接口和 MyStack 类可以被重用。

类型反射和元编程

TypeScript 提供了一些元编程的机制,允许我们在运行时访问类型信息和操作类型。例如:

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

let user = { name: "John", age: 30 };
let name = getProperty(user, "name");
let age = getProperty(user, "age");
console.log(name, age); // 输出 "John 30"

在这个例子中,使用 keyof T 泛型约束 K 的类型为 T 的键,getProperty 函数利用 K 的类型信息来访问 obj 对象的属性。

自定义装饰器

TypeScript 支持自定义装饰器,它可以在类定义、方法定义和属性定义上添加元数据和行为。例如:

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  let original = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${key} with args: ${JSON.stringify(args)}`);
    let result = original.apply(this, args);
    console.log(`Result: ${JSON.stringify(result)}`);
    return result;
  };

  return descriptor;
}

class Calculator {
  @log
  add(a: number, b: number) {
    return a + b;
  }
}

let calculator = new Calculator();
console.log(calculator.add(1, 2)); // 输出 "Calling add with args: [1,2]", "Result: 3", 3

在这个例子中,log 装饰器会输出函数的调用信息和返回结果。

异步编程和 async/await

TypeScriptJavaScript 一样支持异步编程,它提供了一些语法和工具来简化异步处理。例如:

async function fetchData(url: string): Promise<string> {
  let response = await fetch(url);
  let data = await response.text();
  return data;
}

fetchData("https://jsonplaceholder.typicode.com/posts/1")
  .then((data) => console.log(data))
  .catch((error) => console.error(error));

在这个例子中,fetchData 函数使用 async/await 语法Promise 来访问 URL,并返回 Promise<string> 类型的结果。

空值检查

TypeScript 支持对变量进行空值检查,避免运行时出现空指针异常,例如:

let data: string | null = null;

if (data) {
  console.log(data.length);
} else {
  console.warn("Data is null.");
}

字符串模板和表达式

TypeScript 支持字符串模板和表达式,可以方便地拼接字符串和变量,例如:

let name = "John";
let age = 30;

console.log(`Name: ${name}, Age: ${age}`); // 输出 "Name: John, Age: 30"

箭头函数和高阶函数

TypeScript 支持箭头函数和高阶函数,可以简化代码并提高可读性,例如:

let numbers = [1, 2, 3, 4, 5];

let oddNumbers = numbers.filter((n) => n % 2 !== 0);

let doubleNumbers = numbers.map((n) => n * 2);

let sum = numbers.reduce((acc, n) => acc + n, 0);

在这个例子中,箭头函数可以使得代码更加简洁和易于阅读,高阶函数可以使得代码更加灵活和可复用。

泛型推导和类型转换

TypeScript 支持泛型推导和类型转换,可以使得代码更加灵活和可扩展,例如:

function getFirstItem<T>(items: T[]): T | undefined {
  return items.length > 0 ? items[0] : undefined;
}

let numbers = [1, 2, 3, 4, 5];
let firstNumber = getFirstItem(numbers); // 推导为 "number | undefined" 类型

if (firstNumber) {
  let squaredNumber = firstNumber ** 2; // 类型转换为 "number" 类型
  console.log(squaredNumber);
}

在这个例子中,getFirstItem 函数使用泛型来表示数组元素的类型,并尝试推导元素类型,使用类型转换来将元素类型转换为 number 类型

异常处理和断言

TypeScript 支持异常处理和断言,可以使得代码更加健壮和安全,例如:

function divide(a: number, b: number) {
  if (b === 0) {
    throw new Error("Division by zero.");
  }
  return a / b;
}

let result = divide(10, 2);

try {
  let quotient = divide(10, 0);
} catch (error) {
  console.error(error.message);
}

let value: number | string = 10;
console.log((value as number).toFixed(2));

在这个例子中,divide 函数使用异常处理来处理除以零的情况,使用类型断言来将 value 变量转换为 number 类型。

如果以上文章中有任何错误之处,请大佬们不吝赐教,让我学习改正~~~

总结

现在你已经掌握了 TypeScript 的精髓,你的代码不再像个“any”了!让我们一起用 TypeScript 打造更加优秀的前端应用吧!

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