TypeScript:让你的代码不再像个“any”了!
开篇
你是否曾经因为代码中的“any
”而感到无从下手?
你是否曾经因为类型错误而烦恼不已?
别担心,今天我们就来谈谈如何用 TypeScript
打造一个无敌的前端应用!
TypeScript 是一门开源的编程语言,它是 JavaScript 的一个超集,支持其所有的语法和特性,并且在此基础上添加了一些新的语法和特性,使得编写和维护大型应用程序更加简单和可靠。本文将介绍 TypeScript 的常用语法和高阶语法,包括类型、面向对象编程、泛型、元组、枚举、接口、命名空间、模块化以及其他一些有用的特性。
类型
类型是 TypeScript
最重要的一个特性之一,它基于静态类型检查
,可以在编译时发现代码中的类型错误,从而减少在运行时出现的错误。以下是 TypeScript
支持的各种类型以及它们的使用方法:
原始数据类型
在 TypeScript
中,原始数据类型共有 5 种,分别是 boolean
、number
、string
、null
和 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
TypeScript
与 JavaScript
一样支持异步编程
,它提供了一些语法和工具来简化异步处理。例如:
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