【前端】1h 搞定 TypeScript 教程_只说重点
不定期更新,建议关注收藏点赞。
目录
- 简介
- 使用
- 基本类型、类型推断和类型注解
- 接口、类型别名、联合类型
- 类与继承
- 泛型Generics
- React 与 TS
- 进阶
- 高级类型
- 装饰器Decorators
- 模块系统
- TypeScript 编译选项
简介
TypeScript(简称 TS)是一种由微软开发的开源编程语言,是 JavaScript 的超集。在 JavaScript 的基础上增加了静态类型检查等其他功能,使得开发者可以在编写代码时更早地发现潜在的错误,从而提高代码的质量和可维护性。
“超集”是指一种包含原始语言或集合的语言或集合。
比如:如果 A 是 B 的超集,那意味着 A 包含了 B 的所有元素或特性,同时可能还额外包含一些元素或特性。
在 TypeScript 和 JavaScript 的关系中,TypeScript 是 JavaScript 的超集,表现为:
所有合法的 JavaScript 代码 都是有效的 TypeScript 代码,甚至可以直接运行。
TypeScript 在 JavaScript 的基础上添加了类型系统和其他一些功能(如类、接口等),因此 TypeScript 扩展了 JavaScript,提供了更多的特性,但依然保留了 JavaScript 的所有内容。
- 作用:使JS更严谨可靠,提高质量和可维护性
- 主要特点
静态类型:TypeScript 允许你在编写代码时指定变量、函数参数、返回值等的类型。这样可以在编译阶段检查类型错误,避免了 JavaScript 动态类型的潜在问题。
编译为 JavaScript:TypeScript 本身不能直接在浏览器中运行,必须先编译成 JavaScript 代码,而编译后的代码可以在所有支持 JavaScript 的环境中运行。
面向对象编程支持:TypeScript 提供了类、接口、继承等面向对象编程的特性,让代码更加结构化。
开发工具支持:TypeScript 提供了更强大的编辑器支持,比如自动完成、代码提示、跳转到定义等功能,可以提高开发效率。
使用
基本类型、类型推断和类型注解
- 基本类型
TypeScript 的一大优势就是静态类型检查。最基础的是理解 TypeScript 的基本类型和如何使用它们。
基本类型:
number:用于表示数字类型。
string:用于表示字符串类型。
boolean:用于表示布尔值类型。
null 和 undefined:表示空值和未定义的变量。
any:任何类型,禁用类型检查,应该尽量避免使用。
void:通常用于表示函数没有返回值。 - 类型推断:TypeScript 会根据变量的初始值来自动推断变量的类型。
let name = "John"; // 推断为 string
let age = 30; // 推断为 number
- 类型注解
TypeScript 允许你显式指定变量、函数参数和返回值的类型,帮助提前发现错误。
//变量类型注解
let name: string = "Alice";
let age: number = 25;function greet(name: string): string {//函数类型注解return "Hello, " + name;
}
TypeScript 中的数组和元组支持类型注解。
元组是一种特殊的数组,允许存储不同类型的值,且长度固定。(注意区别python元组)
let numbers: number[] = [1, 2, 3];let tuple: [string, number] = ["Alice", 30];
接口、类型别名、联合类型
- 接口用于定义对象的结构,TypeScript 强烈推荐使用接口来描述对象类型。
//对象类型接口
interface Person {name: string;age: number;
}const person: Person = {name: "Alice",age: 30,
};//可选属性和只读属性
interface Person {name: string;age?: number; // 可选属性readonly id: string; // 只读属性
}
- 类型别名 (Type Aliases)
类型别名可以为类型提供一个新的名字,可以用于简化复杂类型或给已有类型起个更直观的名字。
type ID = string | number;function printId(id: ID): void {console.log(id);
}
- 联合类型 (Union Types)
Union 类型允许一个变量或参数可以是多种类型之一。你可以使用 | 符号来定义联合类型。
function printId(id: string | number): void {console.log(id);
}printId(123); // 合法
printId("abc"); // 合法
类与继承
TypeScript 提供了对面向对象编程(OOP,Object Oriented Programming)的良好支持,特别是类和继承。
//类和构造函数
class Person {name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}greet(): string {return `Hello, I'm ${this.name}.`;}
}const person = new Person("Alice", 30);//继承
class Employee extends Person {role: string;constructor(name: string, age: number, role: string) {super(name, age);this.role = role;}describe(): string {return `${this.name} is a ${this.role}`;}
}const employee = new Employee("Bob", 25, "Developer");
泛型Generics
泛型,就是我不确定具体类型,先占个位,等用的时候再指定。
- 作用:泛型允许你定义能够处理多种类型的函数、接口和类,增强代码的复用性和类型安全。
function identity<T>(arg: T): T {return arg;
}
<T>
:占位符,表示"某种类型",但暂时不知道是什么。
arg: T
:这个参数是 T 类型。
: T
:函数返回值也是 T 类型。
//函数
function identity<T>(arg: T): T {return arg;
}let result = identity<string>("Hello");//接口
interface Box<T> {value: T;
}
const box: Box<number> = { value: 123 };
React 与 TS
在 React 项目中,使用 TypeScript 能让你在处理状态、props、组件等方面更加得心应手,避免了类型错误。
项目 | 老写法(React 16-17) | 新写法(React 18+) |
---|---|---|
import React | 必须写 | 不用写了 |
用 React.FC | 常用 | 不推荐,用 Props 明确写 |
useState/useEffect | 基本一致 | 基本一致 |
- 不强制 import React 了
因为从 React 17 新的 JSX 转换开始,编译器自己知道怎么处理 JSX,不需要手动 import。
//没有 import React from 'react',也不用 React.FC
interface Props {name: string;
}
//({ name }: Props)这个{name}是否解构props
//解构的话直接拿到 name,不需要 props.name,更方便。
const MyComponent = ({ name }: Props) => {return <h1>Hello, {name}!</h1>;
}
//未解构
const MyComponent = (props: Props) => {return <h1>Hello, {props.name}!</h1>;
}
- 很多人现在不推荐用 React.FC 来定义组件了
原因:React.FC 会自动加上 children 属性,可能让类型不准确。写法麻烦。 - 没变化:useState 和 useEffect 的类型
- useEffect 基本不用专门加类型,除非你需要在里面用复杂的异步处理。
- useEffect 跟 useState 是完全不一样的设计
useState —— 是泛型函数,因为它需要知道你存的数据类型是什么。
useEffect —— 是普通函数,根本不需要泛型,因为它就是执行副作用(side effect),不是操作数据!
const [count, setCount] = useState<number>(0);
//useState<T>(initialValue) 给初始值加类型是非常标准的用法。
/*
返回值这里没有指定类型是因为
TypeScript 已经根据 useState<number>() 的返回类型,自动推导出来了
指定一次,后面全自动继承。
*/
function useState<T>(initialValue: T): [T, (newValue: T) => void] {let value = initialValue;function setValue(newValue: T) {value = newValue;console.log("新值:", value);}return [value, setValue];
}useEffect(() => {console.log("Component mounted");
}, []);
进阶
高级类型
(例如映射类型、条件类型等),这些通常出现在一些高难度的面试题和库的开发中。
装饰器Decorators
模块系统
在 TypeScript 中,模块和命名空间用于组织代码。模块通过 export 和 import 来进行模块化管理。
// file1.ts
export function greet(name: string) {return `Hello, ${name}!`;
}// file2.ts
import { greet } from './file1';console.log(greet("Alice"));
- 命名空间(Namespace)
用来组织代码的工具,给代码加一个盒子,防止名字冲突。
比如:两个函数,名字都叫 sum,那肯定打架!放到不同的命名空间里面,就互不干扰了。
命名空间是 老时代 TypeScript 管理代码的方法。现在更推荐用 模块(import/export)来组织代码。
namespace MathTools {
//加了 export,外面才能访问里面的函数,不然是私有的。export function sum(a: number, b: number): number {return a + b;}export function multiply(a: number, b: number): number {return a * b;}
}let result1 = MathTools.sum(1, 2); // 3
let result2 = MathTools.multiply(3, 4); // 12
TypeScript 编译选项
深入理解 TypeScript 的编译器选项,优化项目的构建过程,特别是在大型应用中。