1. Typescript
TypeScript 是一种由微软开发的开源编程语言,它是 JavaScript 的超集,为 JavaScript 增加了静态类型系统。
在前端开发中,TypeScript 具有以下重要作用和特点:
1.优势
1. 静态类型检查
- 可以在编译阶段检测出类型错误,减少运行时错误的出现概率。例如,当你试图将一个字符串赋值给一个期望为数字类型的变量时,TypeScript 编译器会报错,提醒你进行修正。
- 增强了代码的可维护性和可读性,使开发者能够更清晰地理解代码的结构和预期的输入输出类型。
2. 更好的代码智能感知和自动补全
- 在现代的代码编辑器中,TypeScript 能够提供更强大的代码智能感知功能。当你输入代码时,编辑器可以根据类型信息准确地提示可能的方法、属性和变量,提高开发效率。
3. 面向大型项目的可扩展性
- 对于大型前端项目,代码的复杂性往往较高。TypeScript 的类型系统有助于组织和管理代码,使不同模块之间的接口更加清晰,降低模块之间的耦合度。
- 方便团队协作,不同开发者可以更好地理解彼此的代码,减少因类型不明确而导致的错误。
4. 与 JavaScript 完全兼容
- TypeScript 代码最终会被编译成 JavaScript 代码,可以在任何支持 JavaScript 的环境中运行。这意味着你可以逐步将现有的 JavaScript 项目迁移到 TypeScript,而无需完全重写代码。
5. 开发工具
1.代码编辑器:vscode
2. 构建工具:webpack,vite
2. Typescript基本语法
1.变量声明和类型注解
1. 变量声明,使用 let
和 const
声明变量,与 JavaScript 类似。
let num = 5;//(可以在后续重新赋值)。
const str = 'Hello';//(一旦赋值不可更改)。
2. 类型注解
- 变量类型注解:使用冒号 “:” 来指定变量的类型,例如
let num: number = 5;
。- 基本类型:
number
(数字)、string
(字符串)、boolean
(布尔值)。例如:let age: number = 30;
let name: string = 'John';
let isStudent: boolean = true;
。 - 数组类型:使用
Array<类型>
或类型[]
表示。例如:let numbers: number[] = [1, 2, 3];
或let names: Array<string> = ['Alice', 'Bob'];
。 - 元组类型:固定长度的数组,其中每个元素的类型可以不同。例如:
let person: [string, number] = ['Tom', 25];
。 - 枚举类型:用于定义一组命名常量。
enum Color { Red, Green, Blue } let myColor: Color = Color.Red;
- 基本类型:
- 函数参数和返回值类型注解:在函数定义时,可以为参数和返回值添加类型注解,如
function add(a: number, b: number): number { return a + b; }
。 - 函数声明,可以指定参数类型和返回值类型。
function add(a: number, b: number): number { return a + b; }
- 可选参数和默认参数,
- 可选参数使用
?
表示,可传可不传。例如:function greet(name: string, age?: number) {... }
- 默认参数直接在参数后赋值。
function multiply(a: number, b = 2): number { return a * b; }
- 可选参数使用
- 箭头函数
- 简洁的函数表达式。
let square = (num: number) => num * num;
- 简洁的函数表达式。
2.接口interface
1.定义接口,用于定义对象的形状,可以描述对象的属性和方法。
interface Person {
name: string;
age: number;
}
let person: Person = { name: 'John', age: 30 };
2.实现接口,类可以通过 implements
关键字实现接口。
class Student implements Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello(): void {
console.log(`Hello, I'm ${this.name}.`);
}
}
3.类class
1.类的定义,包括属性、构造函数和方法。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log('Some sound');
}
}
2.继承,使用 extends
关键字实现继承。
class Dog extends Animal {
bark(): void {
console.log('Woof!');
}
}
3.访问修饰符,public
(公开,默认)、private
(私有,只能在类内部访问)、protected
(受保护,在类内部和子类中可访问)。
class Car {
private engine: string;
constructor(engine: string) {
this.engine = engine;
}
}
- TypeScript 支持面向对象编程的类的概念,包括属性、方法、构造函数等。
class Animal { name: string; constructor(name: string) { this.name = name; } makeSound() { console.log('Some sound'); } }
3. Typescript泛型(Generics)
在 TypeScript 中,泛型是一种强大的工具,它允许你编写可重用的代码组件,同时保持类型安全。
1.什么是泛型
泛型允许你在定义函数、类或接口的时候,不预先指定具体的类型,而是在使用的时候再指定类型。这样可以使代码更加灵活和可复用。
// 下面是一个没有使用泛型的函数:
function identity(arg: number): number {
return arg;
}
// 这个函数只能接受和返回数字类型。如果想要一个可以处理字符串或其他类型的函数,就需要重复编写类似的代码。
// 使用泛型,可以这样写
function identity<T>(arg: T): T {
return arg;
}
// 现在这个函数可以接受任何类型的参数,并返回相同类型的值。
2.泛型的使用场景
1.泛型函数,可以使函数适用于多种不同的类型。可以根据不同的输入类型执行相同的逻辑。例如,一个用于数组操作的函数:
function identity<T>(arg: T): T {
return arg;
}
let result = identity<string>('Hello');
2.泛型类,类也可以使用泛型参数,可以创建通用的数据结构。
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
3.泛型接口:定义具有通用类型参数的接口。
interface KeyValuePair<K, V> {
key: K;
value: V;
}
let pair: KeyValuePair<string, number> = { key: 'age', value: 30 };
3.泛型约束
有时候,你可能需要对泛型的类型进行一些限制。可以使用泛型约束来实现这一点。例如:
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): void {
console.log(arg.length);
}
logLength('hello'); // 5
logLength([1, 2, 3]); // 3
在这个例子中,泛型 T
被约束为必须具有 length
属性的类型。
4. extends
1.类的继承,使用 “extends” 关键字可以实现类的继承。子类继承父类的属性和方法,并可以添加新的属性和方法或重写父类的方法。
class Animal {
move(): void {
console.log('Animal is moving.');
}
}
class Dog extends Animal {
bark(): void {
console.log('Woof!');
}
}
const myDog = new Dog();
myDog.move(); // 继承自 Animal 类的方法
myDog.bark();
// Dog类继承了Animal类,所以Dog的实例可以调用move方法和自己特有的bark方法。
2.接口继承
接口也可以使用 “extends” 来继承其他接口。一个接口可以继承多个接口,合并它们的属性和方法要求。
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square: Square = {
color: 'blue',
sideLength: 5
};
// 这里Square接口继承了Shape接口,所以Square类型的对象必须同时具有color和sideLength属性。
3.泛型约束
这里Square
接口继承了Shape
接口,所以Square
类型的对象必须同时具有color
和sideLength
属性。
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(obj: T): void {
console.log(obj.length);
}
logLength('hello'); // 5
logLength([1, 2, 3]); // 3
// 泛型参数T被约束为必须具有length属性的类型。这样,函数logLength只能接受具有length属性的参数。
4.TypeScript 泛型的最佳实践
1. 提高代码复用性
1. 创建通用的数据结构和函数,使用泛型创建一个通用的栈(Stack)数据结构,可以存储不同类型的数据:
class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
const poppedNumber = numberStack.pop();
const stringStack = new Stack<string>();
stringStack.push('a');
stringStack.push('b');
const poppedString = stringStack.pop();
2.增强类型安全性
1.泛型约束,
- 当你对泛型的类型有一定的要求时,可以使用泛型约束。例如,如果你希望一个泛型函数只接受具有特定属性的类型,可以这样做:
interface HasLength { length: number; } function logLength<T extends HasLength>(obj: T): void { console.log(obj.length); } logLength('hello'); // 5 logLength([1, 2, 3]); // 3
2.泛型接口,
- 定义接口时使用泛型可以提高接口的灵活性和可复用性,同时保证类型安全。例如:
interface KeyValuePair<K, V> { key: K; value: V; } const pair: KeyValuePair<string, number> = { key: 'age', value: 30 };
3.清晰的命名和文档
1. 有意义的泛型名称
class List<TElement> {
private items: TElement[] = [];
add(item: TElement) {
this.items.push(item);
}
//...
}
2. 提供清晰的文档注释
/**
* A function that takes an array of items of type T and a callback function.
* The callback function is applied to each item in the array.
* @param array The array of items.
* @param callback The function to apply to each item.
* @returns An array of the results of applying the callback function.
*/
function mapArray<T>(array: T[], callback: (item: T) => T): T[] {
return array.map(callback);
}
4. 避免过度复杂的泛型使用
5. 与其他 TypeScript 特性结合使用
1. 与类型别名结合
type Pair<T> = [T, T];
const pair: Pair<number> = [1, 2];
2. 与接口继承结合
interface Animal {
name: string;
}
interface Dog<T extends Animal> extends Animal {
breed: T;
}
const myDog: Dog<{ name: 'Labrador', color: 'brown' }> = {
name: 'Buddy',
breed: { name: 'Labrador', color: 'brown' }
};
5. infer
在 TypeScript 中,“infer” 关键字用于在条件类型中进行类型推断。它允许你从现有的类型中提取特定的部分,并在需要的时候使用这些推断出的类型。
1.提取函数返回值类型
可以使用 “infer” 来提取函数的返回值类型。
type ReturnType<T> = T extends (...args: any[]) => infer R? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType<typeof add>; // number
// ReturnType类型通过条件类型和 “infer” 关键字推断出函数的返回值类型。
2.处理数组元素类型
可以提取数组的元素类型。
type ArrayElementType<T> = T extends Array<infer E>? E : never;
const numbers = [1, 2, 3];
type NumberType = ArrayElementType<typeof numbers>; // number
// ArrayElementType类型提取了数组的元素类型。
3. 在复杂类型中进行推断
“infer” 可以在复杂的类型结构中进行推断。
type PromiseReturnType<T> = T extends Promise<infer R>? R : never;
const promise = new Promise<string>((resolve) => {
resolve('Hello');
});
type PromiseResult = PromiseReturnType<typeof promise>; // string
// “infer” 用于推断 Promise 的结果类型。
4.注意事项
1. 只能在条件类型中使用 “infer”。
2. “infer” 的作用范围是在紧跟其后的条件类型中。如果需要在多个地方使用推断出的类型,可以将其存储在一个新的类型别名中。
3. 过度复杂的类型推断可能会使代码难以理解。在使用 “infer” 时,要确保代码的可读性和可维护性。
总之,“infer” 是 TypeScript 中一个强大的工具,可以帮助你进行类型推断,使代码更加灵活和可维护。但在使用时要谨慎,避免过度复杂的类型操作。
// 动态获取函数参数的类型
function test (name: string) {}
// 用ts提取函数的参数类型
type Fn = (name: [string, number, Date]) => void
const t: Fn = (name) => {}
// 正向定义可以这样,但是我们需要反向推导
// 1.首先使用infer
// 2.然后要用extends约束,理解为判断
// 3.使用三目运算符
// 先来监测传进来的参数是否符合我的约束
// 符合的话,返回对应推导出的类型,否则,返回never
type TestParams<F> = F extends (name: infer P) => void ? P : never
// type TestParams<F> = F extends (...args: infer P) => void ? P : never
// type TestParams<F> = F extends (name: infer P, age: infer A) => void ? P : never
let f:TestParams<Fn>