对比
最大区别:ts能在编译时就能发现类型错误的问题,而js只有在系统运行时再通过异常中断来发现
ts的底层仍是js,但ts能够有效减少系统出现问题的概率
特性 | TypeScript | JavaScript |
---|---|---|
类型系统 | 静态类型系统,支持类型注解和类型推断。 | 动态类型系统,类型检查发生在运行时。 |
编译 | 需要经过编译过程,将 TypeScript 转换成 JavaScript。 | 不需要编译,可以直接在浏览器或 Node.js 中执行。 |
类型注解 | 支持类型注解,允许明确地指定变量的类型。 | 不支持类型注解,变量类型由赋值确定。 |
高级特性 | 提供一些 JavaScript 不支持的高级特性,如枚举、泛型、命名空间等。 | JavaScript 没有类似的特性。 |
生态系统 | 逐渐在 JavaScript 的生态系统中获得了广泛的支持和应用。 | 有一个庞大的生态系统,包括各种库和框架。 |
类型检查 | 在编码阶段进行类型检查,可以检测类型错误。 | 类型检查发生在运行时,不能在编码阶段捕获所有类型错误。 |
使用场景 | 适用于大型项目和团队,提高代码的可维护性和可读性。 | 适用于小型项目和快速原型开发,灵活性更高。 |
ts编译器
浏览器以及NodeJs是不认ts代码,所以,需要使用编译器将ts转为js代码
npm i -g typescript
i: 是 install 的简写,表示安装的意思。在 npm 命令中,npm install 用于安装指定的 Node.js 模块或工具包。
-g: 是 global 的缩写,表示全局安装的意思。当指定 -g 选项时,npm 将会把模块安装到全局环境中,而不是当前项目的 node_modules 目录中。全局安装的模块可以在系统的任何位置使用。
编译命令
tsc <文件名>
运行命令
可以使用node的ts-node命令,该命令可以编译运行ts文件
类型注解
在 TypeScript 中,可以使用类型注解来为变量、函数参数、函数返回值等添加类型信息,以帮助编译器进行类型检查和推断,提高代码的可读性和可维护性。
用法很简单,就是在js的变量后面加上冒号和类型注解即可
-
基本类型注解(Basic Types):
number
: 表示数字类型。string
: 表示字符串类型。boolean
: 表示布尔类型。null
: 表示空值类型。undefined
: 表示未定义类型。void
: 表示没有任何返回值的类型。symbol
: 表示符号类型(ES6 新增)。bigint
: 表示大整数类型(ES2020 新增)。
-
复合类型注解(Compound Types):
object
: 表示非原始类型,即除 number、string、boolean、null 和 undefined 之外的类型。array
: 表示数组类型,可以使用type[]
或Array<type>
来表示。tuple
: 表示元组类型,允许表示一个已知元素数量和类型的数组。enum
: 表示枚举类型,用于定义一组命名的常量值。union
: 表示联合类型,表示一个值可以是多种类型中的一种。intersection
: 表示交叉类型,表示一个值可以同时具有多种类型的特性。
-
函数类型注解(Function Types):
(param1: type1, param2: type2, ...) => returnType
: 表示函数类型,指定函数参数和返回值的类型。(type1, type2) => returnType
: 表示函数类型的简写形式,省略参数名。
-
类型别名注解(Type Aliases):
type
: 使用type
关键字定义一个类型别名,可以给复杂的类型起一个简洁的名称。
-
接口注解(Interfaces):
interface
: 使用interface
关键字定义一个接口,用于描述对象的结构和行为。
-
泛型注解(Generics):
<T>
: 表示泛型类型,可以在函数、类、接口中使用泛型来增加代码的灵活性和复用性。
下面是一些 TypeScript 代码示例,演示了如何使用不同类型注解:
- 基本类型注解:
let num: number = 10;
let str: string = "Hello";
let bool: boolean = true;
let n: null = null;
let u: undefined = undefined;
let v: void = undefined; // void 类型只能赋值为 undefined 或 null
let sym: symbol = Symbol("key");
let big: bigint = 100n; // bigint 需要在数字后面添加 "n"
- 复合类型注解:
let obj: object = { name: "John", age: 30 };
let arr: number[] = [1, 2, 3];
let tuple: [string, number] = ["John", 30];
enum Color { Red, Green, Blue }
let color: Color = Color.Red;
let union: string | number = "hello";
let intersection: { a: number } & { b: string } = { a: 1, b: "hello" };
- 函数类型注解:
// 定义函数类型注解
let add: (x: number, y: number) => number;
// 定义函数实现
add = function(x, y) {
return x + y;
};
// 简写形式
let subtract: (x: number, y: number) => number = (x, y) => x - y;
- 类型别名注解:
type MyString = string;
let strAlias: MyString = "Hello";
- 接口注解:
interface Person {
name: string;
age: number;
}
let person: Person = { name: "John", age: 30 };
- 泛型注解:
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("hello");
新增类型
ts新增许多类型
数组
TypeScript 中的数组类型语法包括两种形式:数组类型注解和泛型数组类型。
- 数组类型注解:
数组类型注解是指定数组中元素的类型的一种方式,语法为 type[]
,其中 type
是数组中元素的类型。
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: string[] = ["hello", "world"];
let mixed: (string | number)[] = ["hello", 1, "world", 2];
- 泛型数组类型:
泛型数组类型使用泛型来定义数组的类型,语法为 Array<type>
,其中 type
是数组中元素的类型。
let numbers: Array<number> = [1, 2, 3, 4, 5];
let strings: Array<string> = ["hello", "world"];
let mixed: Array<string | number> = ["hello", 1, "world", 2];
特别的,通过竖线,表示联合类型,表示一个数组中可以包含多种数据类型
自定义类型注解
TypeScript 中的类型别名(Type Aliases)语法格式为使用 type
关键字定义一个新的类型名称,可以基于现有的类型创建新的类型别名。类型别名可以让我们更加清晰和简洁地表示复杂的类型结构,提高代码的可读性和可维护性。
- 基本类型别名:
type MyNumber = number;
type MyString = string;
type MyBoolean = boolean;
在这个例子中,我们定义了三个基本类型的别名 MyNumber
、MyString
、MyBoolean
,分别代表了 number
、string
和 boolean
类型。
- 复合类型别名:
type Point = {
x: number;
y: number;
};
type Person = {
name: string;
age: number;
};
这里我们定义了两个复合类型的别名 Point
和 Person
,分别代表了一个包含 x
和 y
属性的坐标点和一个包含 name
和 age
属性的人员信息。
- 联合类型别名:
type ID = string | number;
这里我们定义了一个联合类型的别名 ID
,可以表示既可以是字符串类型也可以是数字类型的 ID。
- 交叉类型别名:
type Dog = {
name: string;
breed: string;
};
type Cat = {
name: string;
color: string;
};
type Pet = Dog & Cat;
在这个例子中,我们定义了 Dog
和 Cat
两种类型别名,然后使用交叉类型 &
定义了一个新的类型别名 Pet
,表示既具有 Dog
特征又具有 Cat
特征的宠物类型。
- 函数类型别名:
type MyFunction = (x: number, y: number) => number;
const add: MyFunction = (a, b) => a + b;
这里我们定义了一个函数类型别名 MyFunction
,表示接受两个 number
类型参数并返回一个 number
类型的函数,然后使用该别名定义了一个加法函数 add
。
函数类型
TypeScript 中的函数类型可以通过类型注解或类型别名来定义函数的参数类型、返回值类型等信息。
- 使用类型注解:
使用类型注解时,直接在函数参数和返回值的位置指定类型。
function add(x: number, y: number): number {
return x + y;
}
在这个例子中,函数 add
接受两个参数 x
和 y
,它们的类型都是 number
,并且函数返回值的类型也是 number
。
- 使用类型别名:
可以通过类型别名来定义函数类型,使得代码更加清晰和可维护。
type MyFunction = (x: number, y: number) => number;
const add: MyFunction = (a, b) => a + b;
在这个例子中,我们定义了一个函数类型别名 MyFunction
,表示接受两个 number
类型参数并返回一个 number
类型的函数。然后,我们使用该别名来定义一个加法函数 add
。
- 使用泛型:
函数类型中也可以使用泛型来增加灵活性,特别是在处理通用类型的情况下。
type IdentityFunction<T> = (arg: T) => T;
const identity: IdentityFunction<number> = (arg) => arg;
在这个例子中,我们定义了一个函数类型别名 IdentityFunction<T>
,表示接受一个参数并返回相同类型的值的函数。然后,我们使用该别名定义了一个标识函数 identity
,并指定泛型类型为 number
。
参数类型
-
参数类型: 你可以为函数的参数指定类型。例如,
(x: number, y: number) => number
表示这个函数接受两个参数,都是数字类型,返回值也是数字类型。 -
返回值类型: 你可以为函数的返回值指定类型。例如,
(x: number, y: number) => number
中的number
表示这个函数的返回值是数字类型。 -
可选参数和默认参数: 你可以在函数类型中使用可选参数和默认参数。例如,
(x: number, y?: number) => number
表示y
参数是可选的;(x: number, y: number = 0) => number
表示y
参数有默认值为0
。 -
剩余参数: 你可以使用剩余参数来表示接受不定数量的参数。例如,
(x: number, ...rest: number[]) => number
表示这个函数接受一个数字类型的参数x
,以及任意数量的数字类型的剩余参数,返回值是数字类型。
下面是一个示例,演示了如何使用 TypeScript 中的函数类型:
// 定义一个函数类型
type MathOperation = (x: number, y: number) => number;
// 定义一个函数,符合 MathOperation 类型
const add: MathOperation = (x, y) => x + y;
// 定义一个函数,符合 MathOperation 类型
const subtract: MathOperation = (x, y) => x - y;
// 使用 add 和 subtract 函数
console.log(add(5, 3)); // 输出: 8
console.log(subtract(5, 3)); // 输出: 2
对象类型
TypeScript 中的对象类型可以通过接口(Interface)或类型别名(Type Aliases)来定义。对象类型用于描述具有特定属性和属性类型的对象结构。
- 使用接口:
使用接口时,可以定义对象的属性名称和属性类型。
interface Person {
name: string;
age: number;
email?: string; // 可选属性
}
const person: Person = {
name: 'Alice',
age: 30,
email: 'alice@example.com'
};
在这个例子中,我们定义了一个 Person
接口,该接口包含 name
和 age
两个必选属性,以及一个可选属性 email
。然后我们创建了一个符合 Person
接口定义的对象 person
。
- 使用类型别名:
使用类型别名时,可以将对象的结构定义为一个自定义的类型别名。
type Point = {
x: number;
y: number;
};
const point: Point = {
x: 10,
y: 20
};
在这个例子中,我们定义了一个 Point
类型别名,表示具有 x
和 y
两个属性的对象。然后我们创建了一个符合 Point
类型定义的对象 point
。
- 索引签名:
可以使用索引签名来描述具有动态属性的对象结构。
interface Dictionary {
[key: string]: number;
}
const data: Dictionary = {
age: 30,
height: 180,
weight: 75
};
在这个例子中,我们定义了一个 Dictionary
接口,该接口具有字符串类型的索引签名,表示对象的属性名是字符串类型,属性值是数字类型。然后我们创建了一个符合 Dictionary
接口定义的对象 data
。
元组类型
在 TypeScript 中,元组(Tuple)类型表示一个固定长度的数组,其中每个元素的类型可以是不同的。元组类型用于确保数组中每个位置的元素类型符合预期。
- 使用类型注解:
可以通过指定每个位置的元素类型来创建元组类型。
let tuple: [number, string, boolean];
tuple = [10, 'hello', true];
在这个例子中,我们定义了一个元组类型,包含三个位置,分别是数字、字符串和布尔值类型。然后我们创建了一个符合这个类型定义的元组 tuple
。
- 使用类型别名:
也可以使用类型别名来定义元组类型,使得代码更加清晰和可维护。
type MyTuple = [number, string, boolean];
let tuple: MyTuple;
tuple = [10, 'hello', true];
在这个例子中,我们定义了一个元组类型别名 MyTuple
,表示包含三个位置,分别是数字、字符串和布尔值类型的元组。然后我们创建了一个符合 MyTuple
类型定义的元组 tuple
。
元组类型的长度和每个位置的元素类型都是固定的,这使得元组类型在需要固定数量和类型的数组时非常有用。但需要注意的是,在访问元组的元素时,需要确保索引不超出元组的长度,否则会导致 TypeScript 编译错误。
在 TypeScript 中,数组(Array)和元组(Tuple)都是用来存储一系列元素的数据结构,但它们之间有一些重要的区别和异同点:
相同点:
-
存储多个元素: 无论是数组还是元组,它们都可以用来存储多个元素,可以通过索引访问这些元素。
-
支持泛型: 数组和元组都支持泛型,可以在定义时指定元素的类型。
-
可变性: 数组和元组都是可变的数据结构,可以动态添加、删除和修改元素。
不同点:
-
长度:
- 数组:数组的长度是动态变化的,可以根据需要动态增加或减少元素。
- 元组:元组的长度是固定的,在创建时就确定了,不能动态增加或减少元素的个数。
-
类型:
- 数组:数组中的所有元素类型可以相同,也可以不同,但数组本身的类型是
Array
。 - 元组:元组中的每个位置的元素类型是预定义的,元组本身的类型是由元素类型的顺序和数量决定的。
- 数组:数组中的所有元素类型可以相同,也可以不同,但数组本身的类型是
-
表示方式:
- 数组:使用方括号
[ ]
表示,例如[1, 2, 3]
。 - 元组:使用圆括号
( )
表示,例如(1, 'hello', true)
。
- 数组:使用方括号
-
访问元素:
- 数组:可以通过索引访问数组中的元素,索引从 0 开始。
- 元组:同样可以通过索引访问元组中的元素,索引也是从 0 开始。
-
扩展性:
- 数组:由于数组的长度可以动态变化,因此它更适合用来表示集合或列表等动态增长的数据结构。
- 元组:元组的长度固定,适合用来表示固定长度和类型的数据结构,如坐标点
(x, y)
。
总的来说,数组更适合用于表示一系列相同类型的元素的集合,而元组更适合用于表示一组固定长度和类型的元素序列。选择使用数组还是元组取决于数据的特性以及在代码中的具体需求。
类型推断
TypeScript 中的类型推断是指编译器根据代码上下文自动推导出变量的类型,而无需显式地指定类型。类型推断可以减少代码中的冗余,提高代码的可读性和可维护性。
类型推断原理:
-
基于初始化值: 当变量声明时有初始化值时,TypeScript 编译器会根据初始化值推断出变量的类型。
-
基于上下文: 当变量在声明时没有初始化值,但在后续使用过程中有明确的赋值操作,TypeScript 编译器会根据赋值操作推断出变量的类型。
-
最佳通用类型: 当多个类型都符合上下文时,TypeScript 编译器会推断出一个最佳通用类型,使得代码更具可读性。
-
上下文中的类型推断: 当函数的返回值类型可以被上下文推断时,TypeScript 编译器会根据函数的实现推断出函数的返回值类型。
下面是一些示例代码,演示了 TypeScript 中的类型推断原理:
// 基于初始化值的类型推断
let num = 10; // TypeScript 推断 num 的类型为 number
let str = 'hello'; // TypeScript 推断 str 的类型为 string
// 基于上下文的类型推断
let arr = [1, 2, 3]; // TypeScript 推断 arr 的类型为 number[]
arr.push(4); // 合法,因为 TypeScript 推断 arr 的类型为 number[]
// 最佳通用类型的类型推断
let mixedArr = [1, 'hello', true]; // TypeScript 推断 mixedArr 的类型为 (string | number | boolean)[]
let bestType = [1, 2, 'hello']; // TypeScript 推断 bestType 的类型为 (string | number)[]
// 上下文中的类型推断
function add(a: number, b: number) {
return a + b;
}
let result = add(2, 3); // TypeScript 推断 result 的类型为 number
在这些示例中,TypeScript 编译器根据初始化值、赋值操作以及函数的上下文推断出变量的类型,使得代码具有更好的类型安全性和可读性。
枚举类型
TypeScript 中的枚举(Enum)类型是一种用户定义的数据类型,用于定义一组命名的常量值。枚举类型在编译时被转换为 JavaScript 对象,其成员可以是字符串或数字,并且可以具有初始化值。
枚举类型的语法格式如下:
enum EnumName {
member1,
member2,
member3,
// ...
}
其中,EnumName
是枚举类型的名称,member1
、member2
、member3
等是枚举成员的名称,它们可以是字符串或数字。如果枚举成员没有初始化值,则默认从 0 开始递增;如果枚举成员有初始化值,则后续成员的值依次递增。
等效于:
(member1 | member2 | member3)
下面是一个示例代码,演示了 TypeScript 中枚举类型的原理和用法:
// 定义一个名为 Direction 的枚举类型
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 使用枚举成员
let playerDirection: Direction = Direction.Up;
// 访问枚举成员的值
console.log(Direction.Up); // 输出: 0
console.log(Direction[0]); // 输出: "Up"
// 使用枚举成员作为函数参数
function move(direction: Direction) {
switch (direction) {
case Direction.Up:
console.log('向上移动');
break;
case Direction.Down:
console.log('向下移动');
break;
case Direction.Left:
console.log('向左移动');
break;
case Direction.Right:
console.log('向右移动');
break;
default:
console.log('未知方向');
}
}
// 调用函数
move(Direction.Right); // 输出: 向右移动
在这个示例中,Direction
枚举类型定义了四个枚举成员,它们的值分别为 0、1、2、3。可以通过枚举成员的名称或值来访问枚举成员。然后,通过 move
函数使用枚举成员作为参数,根据不同的枚举成员执行相应的操作。