TypeScript 是 JavaScript 的超集,它为 JavaScript 添加了静态类型检查、面向对象编程和其他特性。以下是 TypeScript 的一些常用特性和用法:
- 类型注解
TypeScript 通过类型注解来实现静态类型检查。类型注解可以添加在变量、函数和类的声明后面,用来指定该变量、函数或类的类型。
typescriptCopy code
let message: string = 'Hello, TypeScript!';
function sayHello(name: string): string {
return `Hello, ${name}!`;
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}!`;
}
}
在上面的示例中,变量 message 和函数 sayHello 都使用了类型注解来指定它们的类型。类 Greeter 中的属性 greeting 和方法 greet 也使用了类型注解。
- 接口
TypeScript 支持接口,用来定义对象的类型和结构。接口可以指定对象的属性和方法,以及它们的类型和参数。
typescriptCopy code
interface Person {
name: string;
age: number;
sayHello(): void;
}
class Student implements Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`);
}
}
let tom: Person = new Student('Tom', 18);
tom.sayHello();
在上面的示例中,接口 Person 定义了一个具有 name、age 和 sayHello 属性的对象类型。类 Student 实现了接口 Person,它具有 name、age 属性和 sayHello 方法。变量 tom 的类型为 Person,实际上是一个 Student 实例。
- 泛型
TypeScript 支持泛型,用来定义可以接收任意类型参数的函数和类。
typescriptCopy code
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>('Hello, TypeScript!');
console.log(output);
class Stack<T> {
private items: T[] = [];
push(item: T) {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
}
let stack = new Stack<string>();
stack.push('JavaScript');
stack.push('TypeScript');
console.log(stack.pop());
console.log(stack.pop());
在上面的示例中,函数 identity 和类 Stack 都使用了泛型。函数 identity 可以接收任意类型的参数,并返回相同的类型。类 Stack 可以用来存储任意类型的元素。
- 枚举
TypeScript 支持枚举,用来定义一组命名的常量。
typescriptCopy code
enum Color {
Red,
Green,
Blue,
}
let color: Color = Color.Green;
console.log(color); // 1
在上面的示例中,枚举 Color 定义了三个常量 Red、Green 和 Blue。变量 color 的类型为 Color,实际上是一个枚举值,它的值为 1,对应的是 Green。
- 类型别名
TypeScript 支持类型别名,用来给一个类型起一个新的名称。
typescriptCopy code
type Name = string;
type Age = number;
type Person = {
name: Name;
age: Age;
sayHello(): void;
};
class Student implements Person {
name: Name;
age: Age;
constructor(name: Name, age: Age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`);
}
}
let tom: Person = new Student('Tom', 18);
tom.sayHello();
在上面的示例中,类型别名 Name、Age 和 Person 分别表示字符串、数字和一个具有 name、age 属性和 sayHello 方法的对象类型。类 Student 实现了接口 Person,并使用了类型别名来指定属性 name 和 age 的类型。变量 tom 的类型为 Person,实际上是一个 Student 实例。
- 类型断言
TypeScript 支持类型断言,用来告诉编译器一个值的类型。类型断言有两种形式,一种是尖括号语法,另一种是 as 语法。
typescriptCopy code
let str: any = 'Hello, TypeScript!';
let len1: number = (<string>str).length;
let len2: number = (str as string).length;
console.log(len1, len2); // 19 19
在上面的示例中,变量 str 的类型为 any,可以是任意类型。使用类型断言可以将其转换为字符串类型,并获取其长度。
- Never 类型
TypeScript 支持 Never 类型,用来表示永远不会发生的值。比如,一个抛出异常的函数返回的就是 Never 类型。
typescriptCopy code
function throwError(message: string): never {
throw new Error(message);
}
let message: string = 'Hello, TypeScript!';
if (typeof message !== 'string') {
throwError('Expected a string');
}
console.log(message);
在上面的示例中,函数 throwError 抛出了一个异常,返回的是 Never 类型。变量 message 的类型为字符串,如果它不是字符串类型,就会调用 throwError 抛出异常。
8. 泛型
TypeScript 支持泛型,用来定义函数、类或接口可以支持多种类型的数据。泛型可以提高代码的重用性和灵活性。
typescriptCopy code
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>('Hello, TypeScript!');
let output2 = identity<number>(42);
console.log(output1, output2); // Hello, TypeScript! 42
在上面的示例中,函数 identity 使用了泛型 T,它表示任意类型的数据。参数 arg 的类型和返回值的类型都是泛型类型 T。调用函数 identity 时,可以指定具体的类型参数。
- 类型推断
TypeScript 支持类型推断,即根据代码的上下文推断出变量的类型。当变量没有显式指定类型时,TypeScript 会根据变量的赋值推断出类型。
typescriptCopy code
let str = 'Hello, TypeScript!';
console.log(typeof str); // string
let num = 42;
console.log(typeof num); // number
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
let obj = { name: 'Tom', age: 18 };
console.log(typeof obj); // object
在上面的示例中,变量 str、num、arr 和 obj 都没有显式指定类型,但 TypeScript 根据赋值推断出它们的类型分别为字符串、数字、数组和对象。
- 可选属性
TypeScript 支持可选属性,用来定义对象属性可以存在或不存在。
typescriptCopy code
interface Person {
name: string;
age?: number;
}
let tom1: Person = { name: 'Tom' };
let tom2: Person = { name: 'Tom', age: 18 };
console.log(tom1, tom2); // { name: 'Tom' } { name: 'Tom', age: 18 }
在上面的示例中,接口 Person 定义了一个必须存在的属性 name,和一个可选的属性 age。变量 tom1 只包含必须存在的属性 name,而变量 tom2 包含了必须存在的属性 name 和可选的属性 age。
11. 类型别名
TypeScript 支持类型别名,用来给一个类型起一个新的名字。
typescriptCopy code
type MyString = string;
type MyNumber = number;
let str: MyString = 'Hello, TypeScript!';
let num: MyNumber = 42;
console.log(str, num); // Hello, TypeScript! 42
在上面的示例中,类型别名 MyString 和 MyNumber 分别表示字符串类型和数字类型。变量 str 和 num 分别使用了这两个类型别名。
- 字符串字面量类型
TypeScript 支持字符串字面量类型,用来限制变量的取值范围。
typescriptCopy code
type Gender = 'Male' | 'Female';
interface Person {
name: string;
gender: Gender;
}
let tom: Person = { name: 'Tom', gender: 'Male' };
console.log(tom); // { name: 'Tom', gender: 'Male' }
在上面的示例中,类型别名 Gender 表示只能取值 'Male' 或 'Female'。接口 Person 定义了属性 gender 的类型为 Gender,变量 tom 符合这个类型限制。
- 枚举类型
TypeScript 支持枚举类型,用来表示一组有名字的常量集合。
typescriptCopy code
enum Color {
Red,
Green,
Blue,
}
let c: Color = Color.Green;
console.log(c); // 1
在上面的示例中,枚举类型 Color 定义了三个常量 Red、Green 和 Blue,分别对应的值是 0、1 和 2。变量 c 的类型是枚举类型 Color,可以赋值为枚举常量。
- 类
TypeScript 支持类,用来封装数据和方法,实现面向对象的编程。
typescriptCopy code
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number) {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
let dog = new Dog('Rover');
dog.move(10);
dog.bark();
在上面的示例中,类 Animal 定义了属性 name 和方法 move,表示动物的名字和移动距离。类 Dog 继承自 Animal,并新增了方法 bark,表示狗的叫声。变量 dog 的类型是 Dog,可以调用从 Animal 继承的方法 move 和自己的方法 bark。
- 接口和类的混合类型
TypeScript 支持接口和类的混合类型,用来表示一个既是类又是接口的类型。
typescriptCopy code
interface Counter {
count: number;
increment(): void;
decrement(): void;
}
class MyCounter implements Counter {
count: number = 0;
increment() {
this.count++;
}
decrement() {
this.count--;
}
}
let counter = new MyCounter();
console.log(counter.count); // 0 counter.increment();
console.log(counter.count); // 1 counter.decrement();
console.log(counter.count); // 0
在上面的示例中,接口 Counter 定义了属性 count 和方法 increment 和 decrement,表示计数器的值和增减操作。类 MyCounter 实现了接口 Counter,并实现了对应的方法和属性。变量 counter 的类型是 MyCounter,可以调用接口定义的方法和属性。
16. 泛型 TypeScript 支持泛型,用来增强代码的复用性和灵活性。
typescript function identity<T>(arg: T): T { return arg; }
let result = identity('Hello, TypeScript!');
console.log(result); // Hello, TypeScript!
在上面的示例中,函数 identity 使用了泛型 <T>,表示可以接受任意类型的参数,并返回相同类型的值。变量 result 的类型是字符串类型,因为参数传入的是字符串类型。
typescriptCopy code
interface Pair<T, U> {
first: T;
second: U;
}
let pair: Pair<string, number> = { first: 'one', second: 1 };
console.log(pair); // { first: 'one', second: 1 }
在上面的示例中,接口 Pair 使用了泛型 <T, U>,表示可以接受任意类型的两个参数,并定义了两个属性 first 和 second 分别表示这两个参数的类型。变量 pair 的类型是 Pair<string, number>,表示第一个参数是字符串类型,第二个参数是数字类型。
- 异步编程
TypeScript 支持异步编程,可以使用 async/await 和 Promise 等语法来实现。
typescriptCopy code
function delay(ms: number): Promise<void> {
return new Promise<void>(resolve => setTimeout(resolve, ms));
}
async function hello() {
console.log('Hello');
await delay(1000);
console.log('World!');
}
hello();
在上面的示例中,函数 delay 返回一个 Promise,表示延迟一段时间后执行。函数 hello 使用 async/await 语法,表示等待异步操作完成后再继续执行下一步操作。
- 模块化
TypeScript 支持模块化,可以使用 import/export 语法来组织代码和导出/导入模块。
typescriptCopy code
// moduleA.ts
export function hello() {
console.log('Hello, Module A!');
}
// moduleB.ts
import { hello } from './moduleA';
hello(); // Hello, Module A!
在上面的示例中,模块 moduleA 导出了函数 hello,模块 moduleB 导入了模块 moduleA 中的函数 hello,并调用了这个函数。
19. 运行时类型检查
TypeScript 支持在运行时对变量类型进行检查,可以使用 typeof、instanceof 和 type 等语法来实现。
typescriptCopy code
let x: number = 1;
if (typeof x === 'number') {
console.log('x is a number');
}
class Animal {}
class Dog extends Animal {}
let dog = new Dog();
if (dog instanceof Dog) {
console.log('dog is a Dog');
}
type Person = { name: string, age: number };
let person: unknown = { name: 'Alice', age: 20 };
if (typeof person === 'object' && person !== null && 'name' in person && 'age' in person) {
let p: Person = person as Person;
console.log(p.name, p.age);
}
在上面的示例中,使用 typeof 判断变量 x 是否是数字类型;使用 instanceof 判断变量 dog 是否是 Dog 类型;使用 type 定义了类型 Person,并使用 typeof 和 in 判断变量 person 是否是 Person 类型。
- 类型断言
TypeScript 支持类型断言,可以使用 as 或 <Type> 语法将一个值断言为特定的类型。
typescriptCopy code
let x: unknown = 'Hello, TypeScript!';
let length1 = (x as string).length;
let length2 = (<string>x).length;
console.log(length1); // 19
console.log(length2); // 19
在上面的示例中,变量 x 的类型是 unknown,可以使用 as 或 <string> 将其断言为字符串类型,并获取字符串的长度。
- 高级类型
TypeScript 支持一些高级类型,包括交叉类型、联合类型、条件类型和映射类型等。
typescriptCopy code
type A = { a: string };
type B = { b: number };
type C = A & B;
type D = A | B;
let c: C = { a: 'hello', b: 123 };
let d1: D = { a: 'hello' };
let d2: D = { b: 123 };
type E<T> = T extends string ? number : boolean;
let e1: E<string> = 123;
let e2: E<number> = true;
type F<T> = { [K in keyof T]: string };
let f: F<{ a: number, b: string }> = { a: '1', b: '2' };
在上面的示例中,定义了两个类型 A 和 B,分别表示具有属性 a 和 b 的对象类型。使用交叉类型 C 和联合类型 D 分别表示具有属性 a 和 b 的对象类型和只具有属性 a 或 b 的对象类型。使用条件类型 E<T> 根据类型 T 的不同返回不同的类型。使用映射类型 F<T> 将类型 T 的每个属性的值都映射为字符串类型。
23. 声明文件
TypeScript 中的声明文件用于描述已有的 JavaScript 代码库的类型信息,使得 TypeScript 可以正确地推导出类型信息和进行类型检查。
例如,要使用 jQuery 库,需要先安装对应的声明文件:
cssCopy code
npm install --save-dev @types/jquery
然后在 TypeScript 代码中就可以使用 jQuery 的类型了:
typescriptCopy code
import $ from 'jquery';
$('button').click(function() {
console.log('clicked');
});
在上面的示例中,使用 import 引入了 jQuery 模块,并直接调用 $ 函数和 click 方法,因为已经安装了 @types/jquery 声明文件,TypeScript 可以正确地推导出 $ 和 click 的类型信息。
除了安装现成的声明文件外,还可以自己编写声明文件。声明文件的文件名以 .d.ts 结尾,例如 jquery.d.ts。在该文件中,需要使用 declare 关键字声明要导出的变量、函数、类等的类型信息,以供 TypeScript 使用。
例如,下面是一个简单的声明文件示例:
typescriptCopy code
declare function add(x: number, y: number): number;
declare namespace MyNamespace {
interface MyInterface {
x: number;
y: number;
}
}
在上面的示例中,使用 declare 关键字声明了一个全局函数 add 和一个命名空间 MyNamespace,其中 MyNamespace 中包含一个接口 MyInterface。
声明文件还可以使用 export 关键字导出类型信息,供其他模块使用。例如:
typescriptCopy code
declare module 'my-module' {
export function greet(name: string): void;
}
在上面的示例中,声明了一个名为 my-module 的模块,并导出了一个函数 greet,该函数接收一个字符串参数 name,没有返回值。其他模块可以使用 import 关键字引入该模块,并使用 greet 函数。