简介:
TypeScript 是一种静态类型的 JavaScript 语言,它最初由微软开发并于 2012 年首次公开发布。它扩展了 JavaScript 的语法,为开发大型应用程序而设计。
为什么使用typescript:
-
程序更容易理解(函数或者方法输入输出的参数类型,外部条件等);
-
效率更高,在不同代码块和定义中进行跳转,代码补全等;
-
更少的错误,编译期间能够发现大部分的错误,杜绝一些比较常见的错误;
-
好的包容性,完成兼容 JavaScript,第三方库可以单独编写类型文件;
1. 类型注解和类型推断
类型注解和类型推断是 TypeScript 的核心概念。类型注解是指手动给变量、参数和函数返回类型进行指定,而类型推断是指 TypeScript 根据上下文自动推断变量或函数的类型。
// 类型注解
let myString: string = "Hello World";
// 类型推断
let myNumber = 42;
2. 类型别名和接口
类型别名和接口都用于定义自定义类型,不同之处在于:
- 类型别名可以声明原始值,联合类型,元组以及其它任何你需要手写的类型,而接口则只能声明对象。
- type不会自动合并,接口会。
- 当出现声明同名数据时,type会报错,interface会进行合并。
// 类型别名
type jenkinsID = string | number;
// 接口
interface Person {
name: string;
age: number;
job?: string; // 可选属性
readonly id: number; // 只读属性
}
3. 类和继承
TypeScript 支持面向对象式的编程,包括类和继承。使用关键字 class 可以创建一个新的类,并使用关键字 extends 来实现继承。
class Animal {
public name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`The ${this.name} moved ${distance}m.`);
}
}
class Cat extends Animal {
constructor(name: string) {
super(name);
}
move(distance: number = 5) {
console.log(`The ${this.name} crept ${distance}m.`);
}
}
const cat = new Cat("Tom");
cat.move();
4. 枚举
枚举是一种特殊的类型,它可以用于定义一组命名的常量。在 TypeScript 中,枚举默认是从 0 开始索引,但是可以手动修改索引值。
enum Direction {
Up,
Down,
Left,
Right,
}
const myDirection: Direction = Direction.Up;
console.log(myDirection); // Output: 0
5. 泛型
泛型是一种可重用的代码模板,它可以用于支持多种类型的数据。在 TypeScript 中,使用泛型可以让我们更加灵活地处理不同类型的数据。
function reverse<T>(items: T[]): T[] {
return items.reverse();
}
console.log(reverse<number>([1, 2, 3])); // Output: [3, 2, 1]
console.log(reverse<string>(["hello", "world"])); // Output: ["world", "hello"]
6. 接口继承类
接口可以继承普通的接口,也可以继承一个类的类型,这个类中只能包含方法的声明。
class Car {
constructor(public brand: string, public model: string) {}
start() {
console.log(`Starting ${this.brand} ${this.model}`);
}
stop() {
console.log(`Stopping ${this.brand} ${this.model}`);
}
}
interface ICar extends Car {
wheels: number;
}
const myCar: ICar = {
brand: "Toyota",
model: "Camry",
wheels: 4,
start() {
console.log(`Starting ${this.brand} ${this.model} with ${this.wheels} wheels.`);
}
};
myCar.start();
7. 联合类型和交叉类型
联合类型和交叉类型是 TypeScript 的高级类型概念。联合类型表示一个变量可以是多种类型之一,而交叉类型表示一个变量必须同时满足多种类型的要求。
// 联合类型
let myValue: string | number;
myValue = "hello";
myValue = 42;
// 交叉类型
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge;
const myPerson: Person = {
name: "Tom",
age: 28,
};
8. 函数类型和箭头函数
在 TypeScript 中,函数也可以像变量一样具有类型。函数类型定义了函数的参数类型和返回值类型,箭头函数则是一种简化的函数定义。
// 函数类型
function add(a: number, b: number): number {
return a + b;
}
// 箭头函数
const subtract = (a: number, b: number): number => a - b;
9. 可选变量和默认参数
在 TypeScript 中,可以给函数参数指定可选类型和默认值。可选参数用 ? 标识,而默认参数则使用等号 = 进行定义。
function greet(name?: string, message: string = "Hello"): string {
return `${message}, ${name || "world"}!`;
}
console.log(greet()); // Output: "Hello, world!"
console.log(greet("Tom")); // Output: "Hello, Tom!"
console.log(greet("Tom", "Hi")); // Output: "Hi, Tom!"
10. 类型断言
类型断言是指告诉编译器变量的类型是什么,通常用于处理一些类型不确定的情况。有两种类型断言方式:尖括号语法和 as 语法。
const myValue: any = "hello";
const length1: number = (<string>myValue).length;
const length2: number = (myValue as string).length;
11. 类型保护
在 TypeScript 中,类型保护指的是使用特定的语法结构,以便 TypeScript 编译器可以在一些条件下确定变量的类型。常见的类型保护结构包括 typeof、instanceof 和 in。
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}
12. 引入和导出模块
TypeScript 支持使用模块化的方式进行项目的组织和管理。使用关键字 import 和 export 可以实现模块的引入和导出。
// user.ts 文件
export interface IUser {
id: number;
name: string;
}
// main.ts 文件
import { IUser } from './user';
const myUser: IUser = {
id: 1,
name: "Tom",
};
13. 类型模板字符串
类型模板字符串允许程序员在字符串中嵌入表达式,并在编译时获得类型检查。使用反引号 `` 和 ${} 表示插值表达式。
let name = "Tom";
let age = 28;
let message: string = `My name is ${name} and I'm ${age} years old`;
14. 使用库和类型定义文件
TypeScript 可以使用 JavaScript 库和框架,但如果这些库没有提供 TypeScript 的类型定义文件,则需要手动编写。类型定义文件可以为库和框架提供 TypeScript 类型,以便编译器可以进行类型检查。
// 安装类型定义文件
npm install --save-dev @types/lodash
// 引入库和类型定义文件
import * as _ from 'lodash';
// 使用库
const myArray = _.chunk([1, 2, 3, 4, 5], 2);
console.log(myArray); // Output: [[1, 2], [3, 4], [5]]
15. 面向对象编程
TypeScript 支持面向对象式的编程,类和继承是面向对象编程的核心概念之一。使用关键字 class 定义类,使用关键字 extends 实现继承。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
bark() {
console.log("Woof! Woof!");
}
}
const myDog = new Dog("Tommy");
myDog.move(10);
myDog.bark();
18. 接口
接口是 TypeScript 中一种常用的类型声明方式,可以描述对象、函数等结构的类型。使用 interface 关键字定义接口。
interface Person {
name: string;
age: number;
job?: string;
readonly id: number;
}
const myPerson: Person = {
name: "Tom",
age: 28,
id: 123,
};
myPerson.job = "coder";
19. 函数
函数是 TypeScript 中的一个核心概念,可以定义函数参数类型、函数返回值类型等信息。
function add(a: number, b: number): number {
return a + b;
}
console.log(add(2, 3)); // Output: 5
20. 类型别名
类型别名是一种类型定义方式,允许为现有类型起一个新名字。使用 type 关键字定义类型别名。
type Id = number | string;
function getId(id: Id): Id {
return id;
}
console.log(getId(42)); // Output: 42
console.log(getId("foo")); // Output: "foo"
23. 空值和未定义
TypeScript 支持空值和未定义类型,可以使用 null 或 undefined 表示。
let myValue: string | null = null;
let myAnotherValue: string | undefined;
console.log(myValue); // Output: null
console.log(myAnotherValue); // Output: undefined
24. 非空断言
非空断言是 TypeScript 的一种特殊语法,可以告诉编译器变量不为 null 或 undefined。使用 ! 符号表示非空断言。
let myValue: string | null = "hello";
console.log(myValue!.length); // Output: 5
25. 迭代器和生成器
迭代器和生成器是 TypeScript 的高级概念之一,它们可以帮助实现通用、可重用的代码。迭代器是使用内部状态可遍历数据的一种方法,而生成器则是可以暂停并继续执行的迭代器。
function* fib() {
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
let generator = fib();
console.log(generator.next().value); // Output: 1
console.log(generator.next().value); // Output: 2
console.log(generator.next().value); // Output: 3
结合vue项目中使用ts
- ts的最大意义就是,避免写错、漏写,这样基本能屏蔽掉低级错误
- 编写公用的方法和全局配置对象,用于提醒使用者传穿错参数或者参数的值
- 编写组件的时候用于提示使用者有没有写错props
- 一些第三方库如果是ts编写,可以检测到你有没有调用错方法,写错配置
- 接口与ts
问题一:
export interface listParams {
page:number,
pageSize:10|20|50
}
// 这个接口可以用于后面的类型,引入方式, import type {listParams} from './xxx'
function getList(params:listParams){
return axios({
url:'xxx',
params
})
}
getList({pages:1,pageSize:10}) // pages写错了,会直接提示
相对于不使用ts来说,可以减少很多低级错误
问题二:
// 如果你一开始没有声明类型,那么即使后面又重新赋值了,也会推断提示错误
let listparams = ref({}) // 首先赋值空
listparams.value = {
page:1,
pageSize:10 // 这里赋值了正确得参数
}
getList(listparams) // 虽然后面赋值了正确的参数,但是这里依旧会报错
// 上述问题的解决方式
import type {listParams} from './xxx'
let myListparams = ref({} as listParams) // 首先赋值空
listparams.value = {
page:1,
pageSize:10 // 这里赋值了正确得参数
}
getList(myListparams) // 上面使用断言后,这里就不会报错了
- props与ts
// test.d.ts 在这里声明接口
export interface testProps{
a:number,
b?:string
}
// test.vue
// 引入testProps
defineProps<testProps>();
//除了可以减少低级错误外,还可以梳理开发思路
- 开发中的报错点 , 现阶段类型不匹配,ts报错
// vue文件 声明一个空对象
let list = ref({})
getList({page:1,pageSize:10})
.then(res=>{
list.value = res.data // 假设这里list赋值有a
})
console.log(list.value.a) // 这里就会提示报错,list是一个空对象,list没有a,但实际list中已经赋值了a。
// 要解决这种报错,可以使用断言,先给上你要取得指,如下
interface listData {
a:string // 只需要定义 , 后续会用到得值。当然 ,更好得还是根据接口文档返回得值
}
let list = ref<listData>({} as listData)
- 调用用ts编写的第三方库时,需要定义某个东西为第三方库里的某个类型
// 比如 第三方库类型报错(比如传参报any类型错误),那么如何去寻找这个类型
// 解决方法:
// 1. 将鼠标指针放在该方法上面,会出现第三方库定义的类型,如router.addRoute()的类型就是RouteRecordRaw
// 2. 引入第三方库的类型,import type {RouteRecordRaw} from 'vue-router'
// 3. 使用
function parseRoute(arr:RouteRecordRaw[]){ // 这样就可以了
arr.forEach(item=>{
router.addRoute(item)
})
}
//注:但是有的库,用import type的方式找不到类型,那么解决方式如下:以vue-router为例
//第一步:寻找项目中node_modules中的vue-router中的package.json中的types中的文件地址
- 一些常见的dom类型报错
// 比如
<div @click="(e)=>{
let target = e.target as HTMLElement // 使用类型断言,(因为ts自动推断错误)
target.innerHtml = // 这里如果不使用类型断言 就是报错
}"></div>