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
函数。