MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

TypeScript基础为Angular开发奠基

2022-08-065.9k 阅读

一、TypeScript 基础类型

1.1 布尔类型(boolean)

在 TypeScript 中,布尔类型是最基本的数据类型之一,用于表示真或假。其声明方式非常直观:

let isDone: boolean = false;

这里,我们声明了一个名为 isDone 的变量,指定其类型为 boolean 并初始化为 false。在 Angular 开发中,布尔类型常用于控制组件的显示状态,比如一个模态框是否可见:

import { Component } from '@angular/core';

@Component({
  selector: 'app-modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.css']
})
export class ModalComponent {
  isModalVisible: boolean = false;

  showModal() {
    this.isModalVisible = true;
  }

  hideModal() {
    this.isModalVisible = false;
  }
}

modal.component.html 中可以根据 isModalVisible 的值来决定模态框的显示或隐藏:

<div *ngIf="isModalVisible" class="modal">
  <!-- 模态框内容 -->
</div>
<button (click)="showModal()">显示模态框</button>
<button (click)="hideModal()">隐藏模态框</button>

1.2 数字类型(number)

TypeScript 中的数字类型用于表示各种数值,包括整数和浮点数。声明数字类型变量如下:

let age: number = 25;
let pi: number = 3.14;

在 Angular 应用中,数字类型常用于处理计数器、价格等数据。例如,一个简单的购物车组件中商品数量的处理:

import { Component } from '@angular/core';

@Component({
  selector: 'app-cart-item',
  templateUrl: './cart-item.component.html',
  styleUrls: ['./cart-item.component.css']
})
export class CartItemComponent {
  itemQuantity: number = 1;

  incrementQuantity() {
    this.itemQuantity++;
  }

  decrementQuantity() {
    if (this.itemQuantity > 1) {
      this.itemQuantity--;
    }
  }
}

cart-item.component.html 中:

<div>
  <span>商品数量: {{ itemQuantity }}</span>
  <button (click)="incrementQuantity()">增加</button>
  <button (click)="decrementQuantity()">减少</button>
</div>

1.3 字符串类型(string)

字符串类型用于表示文本数据。可以使用单引号或双引号来定义字符串:

let name: string = 'John Doe';
let message: string = "Hello, world!";

在 Angular 开发中,字符串类型广泛应用于组件的文本显示、用户输入等方面。例如,一个用户登录组件中用户名和密码的输入处理:

import { Component } from '@angular/core';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent {
  username: string = '';
  password: string = '';

  onSubmit() {
    console.log(`用户名: ${this.username}, 密码: ${this.password}`);
  }
}

login.component.html 中:

<form (ngSubmit)="onSubmit()">
  <label>用户名:
    <input type="text" [(ngModel)]="username">
  </label>
  <label>密码:
    <input type="password" [(ngModel)]="password">
  </label>
  <button type="submit">登录</button>
</form>

1.4 数组类型(array)

TypeScript 支持两种方式声明数组。一种是在类型后面加上 [],另一种是使用 Array<类型> 的形式。

// 第一种方式
let numbers: number[] = [1, 2, 3, 4, 5];
// 第二种方式
let names: Array<string> = ['Alice', 'Bob', 'Charlie'];

在 Angular 中,数组常用于存储列表数据,比如文章列表、用户列表等。以一个文章列表组件为例:

import { Component } from '@angular/core';

@Component({
  selector: 'app-article-list',
  templateUrl: './article-list.component.html',
  styleUrls: ['./article-list.component.css']
})
export class ArticleListComponent {
  articles: Array<{ title: string; content: string }> = [
    { title: '第一篇文章', content: '文章内容1' },
    { title: '第二篇文章', content: '文章内容2' }
  ];
}

article-list.component.html 中:

<ul>
  <li *ngFor="let article of articles">
    <h3>{{ article.title }}</h3>
    <p>{{ article.content }}</p>
  </li>
</ul>

1.5 元组类型(tuple)

元组类型允许表示一个固定长度的数组,其中每个元素的类型可以不同。声明元组如下:

let user: [string, number] = ['John', 25];

这里,user 元组的第一个元素是字符串类型,第二个元素是数字类型。在 Angular 开发中,元组可以用于一些特定场景,比如存储坐标信息:

import { Component } from '@angular/core';

@Component({
  selector: 'app-map-marker',
  templateUrl: './map-marker.component.html',
  styleUrls: ['./map-marker.component.css']
})
export class MapMarkerComponent {
  markerPosition: [number, number] = [10.0, 20.0];
}

map-marker.component.html 中可能会根据 markerPosition 来显示地图上的标记位置(实际应用中需要结合地图相关库)。

1.6 枚举类型(enum)

枚举类型用于定义一组命名的常量。它可以让代码更易读和维护。

enum Color {
  Red,
  Green,
  Blue
}

let myColor: Color = Color.Green;

默认情况下,枚举成员从 0 开始自动赋值。也可以手动指定值:

enum Direction {
  Up = 1,
  Down,
  Left,
  Right
}

let myDirection: Direction = Direction.Right;

在 Angular 开发中,枚举可用于表示组件的不同状态,比如一个按钮的不同操作状态:

import { Component } from '@angular/core';

enum ButtonState {
  Normal,
  Loading,
  Disabled
}

@Component({
  selector: 'app-custom-button',
  templateUrl: './custom-button.component.html',
  styleUrls: ['./custom-button.component.css']
})
export class CustomButtonComponent {
  buttonState: ButtonState = ButtonState.Normal;

  onClick() {
    this.buttonState = ButtonState.Loading;
    // 模拟异步操作
    setTimeout(() => {
      this.buttonState = ButtonState.Normal;
    }, 2000);
  }
}

custom - button.component.html 中:

<button [disabled]="buttonState === ButtonState.Disabled || buttonState === ButtonState.Loading" (click)="onClick()">
  {{ buttonState === ButtonState.Loading? '加载中...' : '点击我' }}
</button>

1.7 任意类型(any)

当我们不确定一个变量的类型,或者希望它可以是任意类型时,可以使用 any 类型。

let value: any = 'Hello';
value = 123;

虽然 any 类型提供了很大的灵活性,但过度使用会失去 TypeScript 类型检查的优势。在 Angular 开发中,尽量避免在公共 API 或关键逻辑中使用 any 类型。不过,在处理第三方库或动态数据时,有时可能会用到。例如,在从一个不确定结构的 API 中获取数据时:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-dynamic-data',
  templateUrl: './dynamic-data.component.html',
  styleUrls: ['./dynamic-data.component.css']
})
export class DynamicDataComponent implements OnInit {
  data: any;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http.get('https://example.com/api/data').subscribe((response) => {
      this.data = response;
    });
  }
}

dynamic - data.component.html 中可以根据 data 的实际结构来显示数据。

1.8 空值类型(void)

void 类型表示没有任何类型。通常用于函数返回值类型,当函数没有返回值时使用。

function logMessage(message: string): void {
  console.log(message);
}

在 Angular 组件的一些方法中,如果只是执行某些操作而不返回数据,就可以使用 void 类型。比如一个清除表单数据的方法:

import { Component } from '@angular/core';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html',
  styleUrls: ['./form.component.css']
})
export class FormComponent {
  username: string = '';
  password: string = '';

  clearForm(): void {
    this.username = '';
    this.password = '';
  }
}

form.component.html 中可以添加一个按钮来调用 clearForm 方法。

1.9 空值(null)和未定义(undefined)

在 TypeScript 中,nullundefined 分别表示空值和未定义的值。它们本身有自己的类型,即 null 类型和 undefined 类型。

let a: null = null;
let b: undefined = undefined;

默认情况下,nullundefined 是所有类型的子类型,也就是说,可以把 nullundefined 赋值给其他类型的变量。但在严格模式下(strictNullChecks 开启),这种赋值会报错。在 Angular 开发中,经常会遇到变量可能为 nullundefined 的情况,比如在获取 DOM 元素时:

import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core';

@Component({
  selector: 'app - dom - access',
  templateUrl: './dom - access.component.html',
  styleUrls: ['./dom - access.component.css']
})
export class DomAccessComponent implements AfterViewInit {
  @ViewChild('myElement') myElement: ElementRef<HTMLElement> | null = null;

  ngAfterViewInit() {
    if (this.myElement) {
      this.myElement.nativeElement.style.color = 'red';
    }
  }
}

dom - access.component.html 中:

<div #myElement>这是一个测试元素</div>

1.10 never 类型

never 类型表示永远不会出现的值的类型。通常在函数抛出异常或无限循环时使用。

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

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

在 Angular 开发中,当一个函数预期永远不会正常返回时,可以使用 never 类型。比如在处理错误状态,直接抛出异常中断操作:

import { Component } from '@angular/core';

@Component({
  selector: 'app - error - handling',
  templateUrl: './error - handling.component.html',
  styleUrls: ['./error - handling.component.css']
})
export class ErrorHandlingComponent {
  data: string | null = null;

  getData(): string {
    if (!this.data) {
      throwError('数据未初始化');
    }
    return this.data;
  }
}

这里 throwError 函数返回 never 类型,因为它抛出异常不会正常返回。

二、TypeScript 函数

2.1 函数定义

TypeScript 中函数的定义与 JavaScript 类似,但可以添加类型注解。函数定义有两种常见形式:函数声明和函数表达式。 函数声明

function add(a: number, b: number): number {
  return a + b;
}

这里,add 函数接受两个 number 类型的参数 ab,并返回一个 number 类型的值。

函数表达式

let subtract: (a: number, b: number) => number = function (a: number, b: number): number {
  return a - b;
};

在 Angular 开发中,函数常用于组件的业务逻辑处理。例如,一个计算商品总价的函数:

import { Component } from '@angular/core';

@Component({
  selector: 'app - product - total',
  templateUrl: './product - total.component.html',
  styleUrls: ['./product - total.component.css']
})
export class ProductTotalComponent {
  price: number = 10;
  quantity: number = 3;

  calculateTotal(): number {
    return this.price * this.quantity;
  }
}

product - total.component.html 中可以显示计算结果:

<p>商品总价: {{ calculateTotal() }}</p>

2.2 函数参数

函数参数可以有默认值。当调用函数时没有传入该参数的值,就会使用默认值。

function greet(name: string = 'Guest') {
  console.log(`Hello, ${name}!`);
}

greet(); // 输出: Hello, Guest!
greet('John'); // 输出: Hello, John!

在 Angular 中,这种特性可以用于设置组件的默认配置。例如,一个弹窗组件可以有默认的标题和内容:

import { Component } from '@angular/core';

@Component({
  selector: 'app - popup',
  templateUrl: './popup.component.html',
  styleUrls: ['./popup.component.css']
})
export class PopupComponent {
  title: string = '默认标题';
  content: string = '默认内容';

  constructor(title?: string, content?: string) {
    if (title) {
      this.title = title;
    }
    if (content) {
      this.content = content;
    }
  }
}

在使用该组件时,可以传入自定义的标题和内容,也可以使用默认值。

2.3 剩余参数

剩余参数允许我们将不确定数量的参数作为一个数组来处理。使用 ... 语法来定义剩余参数。

function sum(...numbers: number[]): number {
  return numbers.reduce((acc, num) => acc + num, 0);
}

let result = sum(1, 2, 3, 4, 5); // result 为 15

在 Angular 中,剩余参数可用于处理组件的动态输入。比如一个按钮组组件,接受多个按钮的文本:

import { Component } from '@angular/core';

@Component({
  selector: 'app - button - group',
  templateUrl: './button - group.component.html',
  styleUrls: ['./button - group.component.css']
})
export class ButtonGroupComponent {
  buttons: string[] = [];

  constructor(...buttonTexts: string[]) {
    this.buttons = buttonTexts;
  }
}

button - group.component.html 中:

<div>
  <button *ngFor="let button of buttons">{{ button }}</button>
</div>

2.4 函数重载

函数重载允许我们为同一个函数定义多个不同的签名。编译器会根据调用时传入的参数类型和数量来选择合适的实现。

function print(value: string): void;
function print(value: number): void;
function print(value: any) {
  console.log(value);
}

print('Hello'); // 调用第一个重载
print(123); // 调用第二个重载

在 Angular 开发中,函数重载可以用于处理不同类型数据的相同操作。比如一个日志记录组件,根据传入的数据类型不同进行不同格式的记录:

import { Component } from '@angular/core';

@Component({
  selector: 'app - logger',
  templateUrl: './logger.component.html',
  styleUrls: ['./logger.component.css']
})
export class LoggerComponent {
  log(message: string): void;
  log(message: number): void;
  log(message: any) {
    if (typeof message ==='string') {
      console.log(`字符串日志: ${message}`);
    } else if (typeof message === 'number') {
      console.log(`数字日志: ${message}`);
    }
  }
}

在组件使用中可以调用 log 方法并传入不同类型的数据。

三、TypeScript 接口

3.1 接口基础

接口是 TypeScript 中非常重要的概念,它用于定义对象的形状(结构)。通过接口,我们可以明确对象应该具有哪些属性以及这些属性的类型。

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

let user: User = {
  name: 'John Doe',
  age: 25
};

在 Angular 开发中,接口常用于定义服务返回的数据结构、组件输入输出属性的类型等。例如,一个用户服务返回用户信息:

import { Injectable } from '@angular/core';

interface User {
  id: number;
  name: string;
  email: string;
}

@Injectable({
  providedIn: 'root'
})
export class UserService {
  getUser(): User {
    return {
      id: 1,
      name: 'Jane Smith',
      email: 'jane@example.com'
    };
  }
}

在组件中使用该服务:

import { Component } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - user - profile',
  templateUrl: './user - profile.component.html',
  styleUrls: ['./user - profile.component.css']
})
export class UserProfileComponent {
  user: User;

  constructor(private userService: UserService) {
    this.user = this.userService.getUser();
  }
}

user - profile.component.html 中可以显示用户信息:

<div>
  <p>姓名: {{ user.name }}</p>
  <p>邮箱: {{ user.email }}</p>
</div>

3.2 可选属性

接口中的属性可以是可选的,使用 ? 来表示。

interface Product {
  name: string;
  price: number;
  description?: string;
}

let product: Product = {
  name: '手机',
  price: 5000
};

在 Angular 组件开发中,可选属性很有用。比如一个商品展示组件,描述信息可能不是必填的:

import { Component } from '@angular/core';

interface Product {
  name: string;
  price: number;
  description?: string;
}

@Component({
  selector: 'app - product - display',
  templateUrl: './product - display.component.html',
  styleUrls: ['./product - display.component.css']
})
export class ProductDisplayComponent {
  product: Product = {
    name: '笔记本电脑',
    price: 8000,
    description: '高性能笔记本'
  };
}

product - display.component.html 中:

<div>
  <h2>{{ product.name }}</h2>
  <p>价格: {{ product.price }}</p>
  <p *ngIf="product.description">{{ product.description }}</p>
</div>

3.3 只读属性

接口中可以定义只读属性,使用 readonly 关键字,一旦赋值后就不能再修改。

interface Point {
  readonly x: number;
  readonly y: number;
}

let point: Point = {
  x: 10,
  y: 20
};
// point.x = 20; // 报错,不能修改只读属性

在 Angular 中,对于一些不可变的数据结构可以使用只读属性。比如一个配置对象,其某些属性在初始化后不应被修改:

import { Component } from '@angular/core';

interface AppConfig {
  readonly appName: string;
  readonly version: string;
}

const config: AppConfig = {
  appName: 'My Angular App',
  version: '1.0.0'
};

@Component({
  selector: 'app - config - display',
  templateUrl: './config - display.component.html',
  styleUrls: ['./config - display.component.css']
})
export class ConfigDisplayComponent {
  config = config;
}

config - display.component.html 中显示配置信息:

<div>
  <p>应用名称: {{ config.appName }}</p>
  <p>版本: {{ config.version }}</p>
</div>

3.4 函数类型接口

接口不仅可以定义对象的结构,还可以定义函数的类型。

interface AddFunction {
  (a: number, b: number): number;
}

let add: AddFunction = function (a: number, b: number): number {
  return a + b;
};

在 Angular 开发中,函数类型接口可以用于定义组件中回调函数的类型。比如一个可点击按钮组件,接受一个点击回调函数:

import { Component } from '@angular/core';

interface ClickCallback {
  (): void;
}

@Component({
  selector: 'app - clickable - button',
  templateUrl: './clickable - button.component.html',
  styleUrls: ['./clickable - button.component.css']
})
export class ClickableButtonComponent {
  clickHandler: ClickCallback;

  constructor(clickHandler: ClickCallback) {
    this.clickHandler = clickHandler;
  }
}

clickable - button.component.html 中:

<button (click)="clickHandler()">点击我</button>

在使用该组件时传入具体的回调函数:

import { Component } from '@angular/core';

@Component({
  selector: 'app - parent - component',
  templateUrl: './parent - component.html',
  styleUrls: ['./parent - component.css']
})
export class ParentComponent {
  onButtonClick() {
    console.log('按钮被点击了');
  }
}

parent - component.html 中:

<app - clickable - button [clickHandler]="onButtonClick"></app - clickable - button>

四、TypeScript 类

4.1 类的基础

类是面向对象编程的核心概念之一。TypeScript 支持类的定义,包括属性、方法、构造函数等。

class Person {
  name: string;
  age: number;

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

  greet() {
    console.log(`Hello, I'm ${this.name} and I'm ${this.age} years old.`);
  }
}

let person = new Person('John Doe', 25);
person.greet();

在 Angular 开发中,组件就是基于类的。每个 Angular 组件都是一个类,带有特定的装饰器(如 @Component)。例如,一个简单的用户信息组件:

import { Component } from '@angular/core';

@Component({
  selector: 'app - user - info',
  templateUrl: './user - info.component.html',
  styleUrls: ['./user - info.component.css']
})
export class UserInfoComponent {
  name: string = 'Jane Smith';
  age: number = 30;

  constructor() {}

  displayInfo() {
    console.log(`姓名: ${this.name}, 年龄: ${this.age}`);
  }
}

user - info.component.html 中:

<button (click)="displayInfo()">显示信息</button>

4.2 继承

类可以通过继承来复用代码。使用 extends 关键字来实现继承。

class Animal {
  name: string;

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

  speak() {
    console.log(`${this.name} makes a sound.`);
  }
}

class Dog extends Animal {
  breed: string;

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

  bark() {
    console.log(`${this.name} barks.`);
  }
}

let dog = new Dog('Buddy', 'Golden Retriever');
dog.speak();
dog.bark();

在 Angular 中,继承常用于创建具有公共功能的基类组件,然后其他组件继承自该基类。比如一个具有通用样式和行为的基类表单组件,其他具体表单组件继承自它:

import { Component } from '@angular/core';

@Component({
  selector: 'app - base - form',
  templateUrl: './base - form.component.html',
  styleUrls: ['./base - form.component.css']
})
export class BaseFormComponent {
  formTitle: string = '默认表单';

  constructor() {}

  submitForm() {
    console.log('表单提交');
  }
}
import { Component } from '@angular/core';

@Component({
  selector: 'app - login - form',
  templateUrl: './login - form.component.html',
  styleUrls: ['./login - form.component.css']
})
export class LoginFormComponent extends BaseFormComponent {
  constructor() {
    super();
    this.formTitle = '登录表单';
  }
}

login - form.component.html 中可以使用从基类继承的属性和方法。

4.3 访问修饰符

TypeScript 提供了三种访问修饰符:publicprivateprotected

  • public:默认的访问修饰符,属性和方法可以在类的内部和外部访问。
  • private:属性和方法只能在类的内部访问。
  • protected:属性和方法可以在类的内部以及子类中访问。
class Car {
  public brand: string;
  private model: string;
  protected year: number;

  constructor(brand: string, model: string, year: number) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }

  getDetails() {
    return `${this.brand} ${this.model} (${this.year})`;
  }
}

class SportsCar extends Car {
  constructor(brand: string, model: string, year: number) {
    super(brand, model, year);
  }

  getSportsCarDetails() {
    return `Sports Car: ${this.getDetails()}`;
  }
}

let car = new Car('Toyota', 'Corolla', 2020);
console.log(car.brand);
// console.log(car.model); // 报错,model 是 private
// console.log(car.year); // 报错,year 是 protected

let sportsCar = new SportsCar('Ferrari', '488 GTB', 2019);
console.log(sportsCar.getSportsCarDetails());

在 Angular 开发中,合理使用访问修饰符可以保护组件的内部状态和逻辑。比如,一个组件内部的某些方法或属性不希望被外部组件直接访问,可以设置为 privateprotected

4.4 静态成员

类的静态成员属于类本身,而不是类的实例。使用 static 关键字来定义静态属性和方法。

class MathUtils {
  static PI: number = 3.14159;

  static calculateCircleArea(radius: number): number {
    return this.PI * radius * radius;
  }
}

let area = MathUtils.calculateCircleArea(5);
console.log(area);

在 Angular 开发中,静态成员可用于提供一些全局的工具方法或常量。比如一个常量类,包含应用中常用的配置常量:

export class AppConstants {
  static API_URL: string = 'https://example.com/api';
  static DEFAULT_PAGE_SIZE: number = 10;
}

在其他组件或服务中可以直接使用这些静态常量:

import { Component } from '@angular/core';
import { AppConstants } from './app.constants';

@Component({
  selector: 'app - api - call',
  templateUrl: './api - call.component.html',
  styleUrls: ['./api - call.component.css']
})
export class APICallComponent {
  constructor() {
    console.log(`API 地址: ${AppConstants.API_URL}`);
  }
}

五、TypeScript 泛型

5.1 泛型基础

泛型是 TypeScript 中非常强大的特性,它允许我们在定义函数、接口或类时使用类型参数。这样可以使代码更加灵活和可复用。

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

let result = identity<number>(5);
let strResult = identity<string>('Hello');

在 Angular 开发中,泛型常用于服务和组件的通用操作。比如一个通用的获取数据的服务:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class GenericService {
  constructor(private http: HttpClient) {}

  get<T>(url: string): Promise<T> {
    return this.http.get<T>(url).toPromise();
  }
}

在组件中使用该服务:

import { Component } from '@angular/core';
import { GenericService } from './generic.service';

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

@Component({
  selector: 'app - user - fetch',
  templateUrl: './user - fetch.component.html',
  styleUrls: ['./user - fetch.component.css']
})
export class UserFetchComponent {
  user: User;

  constructor(private genericService: GenericService) {
    this.genericService.get<User>('https://example.com/api/user').then((data) => {
      this.user = data;
    });
  }
}

5.2 泛型接口

可以定义泛型接口,使接口的类型参数化。

interface KeyValuePair<K, V> {
  key: K;
  value: V;
}

let pair: KeyValuePair<string, number> = { key: 'count', value: 10 };

在 Angular 开发中,泛型接口可用于定义一些通用的数据结构。比如一个分页结果的接口:

interface PaginationResult<T> {
  data: T[];
  total: number;
}

interface Product {
  name: string;
  price: number;
}

let productPagination: PaginationResult<Product> = {
  data: [
    { name: '商品1', price: 100 },
    { name: '商品2', price: 200 }
  ],
  total: 2
};

5.3 泛型类

泛型类允许我们创建具有通用类型的类。

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

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

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

let numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
let popped = numberStack.pop();

在 Angular 开发中,泛型类可以用于实现一些通用的数据结构或操作。比如一个通用的缓存类:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class CacheService<T> {
  private cache: { [key: string]: T } = {};

  set(key: string, value: T) {
    this.cache[key] = value;
  }

  get(key: string): T | undefined {
    return this.cache[key];
  }
}

在组件中使用该缓存服务:

import { Component } from '@angular/core';
import { CacheService } from './cache.service';

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

@Component({
  selector: 'app - user - cache',
  templateUrl: './user - cache.component.html',
  styleUrls: ['./user - cache.component.css']
})
export class UserCacheComponent {
  user: User;

  constructor(private cacheService: CacheService<User>) {
    let cachedUser = this.cacheService.get('user - 1');
    if (cachedUser) {
      this.user = cachedUser;
    } else {
      this.user = { name: 'New User', age: 20 };
      this.cacheService.set('user - 1', this.user);
    }
  }
}

5.4 泛型约束

有时我们需要对泛型类型参数进行约束,以确保它具有某些特定的属性或方法。可以使用 extends 关键字来实现泛型约束。

interface Lengthwise {
  length: number;
}

function printLength<T extends Lengthwise>(arg: T) {
  console.log(arg.length);
}

printLength('Hello');
printLength([1, 2, 3]);
// printLength(123); // 报错,number 类型没有 length 属性

在 Angular 开发中,泛型约束可用于确保传入的类型满足一定的条件。比如一个通用的显示组件,要求传入的数据对象具有 displayName 属性:

import { Component } from '@angular/core';

interface Displayable {
  displayName: string;
}

@Component({
  selector: 'app - display - generic',
  templateUrl: './display - generic.component.html',
  styleUrls: ['./display - generic.component.css']
})
export class DisplayGenericComponent<T extends Displayable> {
  data: T;

  constructor(data: T) {
    this.data = data;
  }
}

display - generic.component.html 中:

<div>
  <p>{{ data.displayName }}</p>
</div>

在使用该组件时,传入的数据对象必须满足 Displayable 接口的约束。

通过以上对 TypeScript 基础的深入讲解和代码示例,相信读者对于如何利用 TypeScript 为 Angular 开发奠定坚实基础有了更清晰的认识。在实际的 Angular 项目开发中,熟练运用 TypeScript 的这些特性将大大提高代码的质量、可维护性和可扩展性。