青训营前端 | 青训营

80 阅读9分钟

青训营上课笔记

四.TypeScript

TypeScript是一种由微软开发的开源编程语言,它是JavaScript的超集,添加了静态类型检查和其他一些语言特性。在WEB开发中,TypeScript可以帮助开发者编写更健壮、可维护和可扩展的代码。

1.基本类型

// TypeScript支持JavaScript的基本类型,如number、string、boolean、null、undefined和object等。在声明变量时,可以明确指定变量的类型。
let age: number = 25;
let name: string = "John";
let isStudent: boolean = true;
let person: object = {age: 25, name: "John"};

2.接口

// TypeScript中的接口可以用来定义一个对象的结构。它们可以包含属性、方法和可选属性。
interface Person{
	age: number;
	name: string;
	sayHello:()=>{
		soncole.log("Hello!")
	},
	address: "123 ABC Street"
};

3.类

// TypeScript支持面向对象编程的概念,可以使用类来创建对象。类可以有属性、方法和构造函数。
class Person{
	age: number;
	name: string;
	constructor(age:number, name:string){
		this.age = age;
		this.name = name;
	}
	sayHello(){
		console.log("Hello!");
	}
}

let person = new Person(25, "John");
person.sayHello();

4.模块化

// TypeScript支持模块化开发,可以使用import和export关键字导入和导出模块。
// math.ts
export function add(a: number, b: number): number{
	return a + b;
}

export function substract(a: number, b: number): number{
	return a - b;
}

// math.ts
import{add, substract}from"./math";
console.log(add(2, 3)); // 输出5
console.log(substract(5, 2)); // 输出3

5.泛型

// TypeScript支持泛型,可以创建可重用的、类型安全的代码。
function reverse<T>(array: T[]):T[]{
	return array.reverse();
}

let numbers = [1, 2, 3, 4, 5];
let reversedNumbers = reverse(numbers);
console.log(reversedNumbers); // 输出[5, 4, 3, 2, 1]

let strings = ["apple", "banana", "cherry"];
let reversedStrings = reverse(strings);
console.log(reversedStrings); // 输出["cherry", "banana", "apple"]

6.类型注解和类型推断

//TypeScript允许开发者明确指定变量的类型,这被称为类型注解。同时,TypeScript还可以根据上下文自动推断变量的类型,这被称为类型推断。

let age: number = 25; 
// 在这个例子中,我们明确指定了age变量的类型为number。

let name = "John";
// 在这个例子中,TypeScript可以根据赋值表达式自动推断出name变量的类型为string。

7.类型别名和联合类型

// TypeScript支持使用type关键字定义类型别名,可以将复杂的类型命名为一个简单的别名。
type Name = string;
type Age = number;

let name: Name = "John";
let age: Age = 25;
// 在这个例子中,Name和Age被定义为类型别名,它们分别表示string和number。

// TypeScript还支持联合类型,可以表示一个值可以具有多个类型中的一个。
type NumberOrString = number | string;

let value: NumberOrString = 123; // 或者 "abc"
// 在这个例子中,NumberOrString表示一个值可以是number或string类型。

8.接口的继承

// TypeScript中的接口可以继承其他接口,通过extends关键字实现。这样可以创建更具体的接口定义。例如:
interface Animal {
 eat: () => void;
}

interface Cat extends Animal {
 meow: () => void;
}

let cat: Cat = {
 eat: () => {
  console.log("Cat is eating.");
 },
 meow: () => {
  console.log("Meow!");
 }
};

// 在这个例子中,Cat接口继承了Animal接口,所以cat对象需要实现eat和meow两个方法。

9.类的继承

// TypeScript支持类的继承,可以通过extends关键字创建一个类继承另一个类。
class Animal {
 eat() {
  console.log("Animal is eating.");
 }
}

class Cat extends Animal {
 meow() {
  console.log("Meow!");
 }
}

let cat = new Cat();
cat.eat(); // 输出:Animal is eating.
cat.meow(); // 输出:Meow!

// 在这个例子中,Cat类继承了Animal类的属性和方法,所以cat对象可以调用eat和meow方法。

10.类型断言

// TypeScript中的类型断言用于告诉编译器某个值的具体类型。
// 有两种方式可以进行类型断言:
	a.尖括号语法 <类型>:例如,(variable as string)
	b.关键字:例如,(variable as string)
// 类型断言可以在需要明确指定类型的情况下使用,比如当你知道一个更具体的类型会被赋值给一个变量。
let value: any = "Hello TypeScript!";
let length: number = (<string>value).length;
// 在上面的例子中,我们将value变量的类型断言为string,然后使用length属性获取字符串的长度。

11.可选属性和只读属性

// TypeScript的接口和类中可以定义可选属性和只读属性。可选属性用于表示一个属性不是必须的,而只读属性可以在创建后不能被更改。

interface Person {
 name: string;
 age?: number; // 可选属性
}

let person1: Person = { name: "John" };
let person2: Person = { name: "Tom", age: 25 };

class Car {
 readonly brand: string; // 只读属性

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

let car = new Car("Toyota");
console.log(car.brand); // 输出:Toyota
// car.brand = "Honda"; // 错误!只读属性不能被重新赋值


// 在上面的例子中,`age`属性是可选的,可以不提供。而`brand`属性是只读的,一旦被赋值后不能再次修改。

12.命名空间和模块

// TypeScript中的命名空间(namespace)和模块(module)用于组织和管理代码。
// 命名空间可以将相关的代码组织在一起,避免命名冲突。
// 模块则是一种更通用的组织代码的方式,可以用来划分功能模块,并且可以使用`import`和`export`语法导入和导出模块。

//命名空间的例子:
namespace Math {
 export function add(a: number, b: number): number {
  return a + b;
 }
}

console.log(Math.add(2, 3)); // 输出:5


//模块的例子:
// math.ts
export function add(a: number, b: number): number {
 return a + b;
}

export function subtract(a: number, b: number): number {
 return a - b;
}

// main.ts
import { add, subtract } from "./math";
console.log(add(2, 3)); // 输出:5
console.log(subtract(5, 2)); // 输出:3

// 在上面的例子中,`math.ts`文件定义了`add`和`subtract`函数,并通过`export`关键字导出。然后在`main.ts`文件中使用`import`语法导入这两个函数。

13.结合HTML与CSS

a.类型安全的DOM操作

// TypeScript可以帮助我们在DOM操作时实现类型安全。
// 通过使用TypeScript的类型推断和类型注解功能,我们可以明确指定DOM元素的类型,以及相应的属性和方法。
const button = document.querySelector("#myButton") as HTMLButtonElement;
button.addEventListener("click", () => {
  button.disabled = true;
});

// 在这个例子中,我们使用`querySelector`方法选择一个按钮元素,并将其断言为`HTMLButtonElement`类型。这样就可以确保我们可以访问和设置按钮的属性和方法,例如将按钮禁用。

b.使用接口定义组件的属性和状态

// TypeScript的接口可以用于定义组件的属性和状态的结构。
// 这样可以提供类型检查和自动补全的好处。
interface ButtonProps {
  text: string;
  disabled?: boolean;
  onClick: () => void;
}

class Button {
  private props: ButtonProps;

  constructor(props: ButtonProps) {
    this.props = props;
  }

  render() {
    const button = document.createElement("button");
    button.innerText = this.props.text;
    button.disabled = this.props.disabled || false;
    button.addEventListener("click", this.props.onClick);
    return button;
  }
}

const button = new Button({
  text: "Click me",
  onClick: () => {
    console.log("Button clicked");
  }
});
document.body.appendChild(button.render());

// 在这个例子中,我们使用`ButtonProps`接口定义了按钮组件的属性结构。
// 然后我们创建了一个`Button`类,并使用`ButtonProps`指定了构造函数的参数类型和渲染方法的返回类型。
// 这样可以确保我们在使用按钮组件时传递了正确的属性,并且属性的类型是符合预期的。

c.结合模块化的开发

// TypeScript的模块化功能可以帮助我们将代码组织成可重用的模块,并通过`import`和`export`语法进行导入和导出。
// 结合HTML和CSS,我们可以将相关的代码、样式和模板放在同一个模块中。
import "./button.css";

interface ButtonProps {
  // 属性定义...
}

class Button {
  // 类定义...
}

export default Button;

// 在这个例子中,我们使用`import`语法导入了一个名为`button.css`的样式文件。然后我们定义了一个按钮组件,并在需要使用该组件的地方通过`import`语法进行导入。

d.类型化框架和库的使用

// TypeScript可以与许多流行的前端框架和库进行无缝集成,从而提供更好的类型支持和开发体验。
// 例如,Angular是一个使用TypeScript编写的强大的前端框架,它提供了丰富的类型定义和类型检查。
// Vue.js也有一个专门为TypeScript设计的官方插件,可以使Vue.js应用程序具备类型安全和编译时检查的优势。

// 使用TypeScript编写的Angular组件
@Component({
  selector: 'app-button',
  template: '<button (click)="onClick()">{{ text }}</button>'
})
export class ButtonComponent {
  @Input() text: string;
  @Output() clicked: EventEmitter<void> = new EventEmitter<void>();

  onClick(): void {
    this.clicked.emit();
  }
}

// 使用TypeScript编写的Vue.js组件
@Component
export default class ButtonComponent extends Vue {
  @Prop() text!: string;

  onClick(): void {
    this.$emit('clicked');
  }
}

e.类型化的HTTP请求

// 在前端开发中,与服务器进行通信是很常见的任务,使用TypeScript可以使HTTP请求更具类型安全性。
// 例如,可以使用`fetch`函数结合`async/await`语法来发起异步HTTP请求。

interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

async function fetchTodo(): Promise<Todo[]> {
  const response = await fetch('https://api.example.com/todos');
  const data = await response.json();
  return data as Todo[];
}

const todos = await fetchTodo();
console.log(todos);

// 在这个示例中,我们定义了一个`Todo`接口来表示待办事项的数据结构。
// 然后,使用`fetch`函数发起HTTP请求,并使用`await`等待数据的返回。
// 最后,我们将返回的数据转换为`Todo[]`类型。

f.类型定义文件(Type Declaration Files)

// 在与第三方JavaScript库或框架集成时,可以使用类型定义文件(.d.ts文件)来描述这些库的类型信息,从而为TypeScript提供类型检查和代码提示。
// 类型定义文件提供了函数、类、接口和模块的声明,以及导出的类型定义。

// 例如,假设我们使用了一个第三方库`lodash`,我们可以创建一个`lodash.d.ts`文件来描述它的类型信息:
// lodash.d.ts
declare module 'lodash' {
  export function chunk(array: any[], size?: number): any[][];
  export function debounce(func: Function, wait?: number): Function;
  // 其他函数和类型声明...
}
// 在这个例子中,我们使用`declare module`语法来声明我们正在描述的模块(`'lodash'`)。然后我们通过`export`语法来导出函数和类型的声明。这样,当我们在TypeScript中导入`lodash`时,就可以进行类型检查和代码提示。

g.类型别名(Type Aliases)

// 类型别名可以用来为类型创建一个新的名称,使代码更加可读和可维护。我们可以使用`type`关键字来定义类型别名。
type Person = {
  name: string;
  age: number;
};

type Point = [number, number];
// 在这个例子中,我们创建了两个类型别名。`Person`是一个对象类型的别名,它具有`name`和`age`属性。`Point`是一个元组类型的别名,它表示具有两个数字的数组。

h.keyof操作符和索引类型查询

// TypeScript支持`keyof`操作符和索引类型查询,这让我们可以通过类型变量来访问对象的属性。使用这种方式可以在编译时获得属性的名称,从而避免运行时的错误。
type Person = {
  name: string;
  age: number;
};

function getProperty(obj: Person, key: keyof Person) {
  return obj[key];
}

const person: Person = { name: 'John', age: 25 };
const name = getProperty(person, 'name');
console.log(name); // 输出:John

// 在这个例子中,我们使用`keyof Person`来获得`Person`对象的所有属性名称。然后我们编写了一个函数`getProperty`,它接收一个对象和一个属性名称,然后返回该属性的值。