TypeScript
问题1: TypeScript和JavaScript有什么区别?
TypeScript是JavaScript的一个超集,它添加了静态类型定义。TypeScript在编译时进行类型检查,而JavaScript是动态类型的,类型检查是在运行时进行的。TypeScript的类型系统允许开发者在代码中定义变量和函数参数的类型,从而提高代码的可读性和可维护性。
let message: string = 'Hello, TypeScript';
console.log(message);
问题2: 什么是TypeScript中的接口(Interfaces)?
接口是TypeScript的一个核心原则之一,它允许你定义对象的形状。接口可以用来描述对象的必需和可选属性,方法签名,甚至其他类型的索引签名。它们主要用于静态类型检查和文档目的。
interface User {
name: string;
age?: number; // 可选属性
}
function greet(user: User) {
console.log('Hello, ' + user.name);
}
问题3: 解释TypeScript中的泛型。
泛型提供了一种方法来创建可重用的组件,这些组件可以支持多种类型而不是单一类型。通过使用泛型,你可以在函数、接口、类中使用类型变量,这些类型变量在使用时才确定具体的类型。
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString");
问题4: TypeScript中的枚举(Enum)有什么用途?
枚举是TypeScript提供的一种数据类型,它允许开发者为一组数值定义友好的名称。枚举在处理一组相关的常量值时非常有用,提高了代码的可读性和可维护性。
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
问题5: 解释TypeScript中的联合类型和交叉类型。
联合类型和交叉类型是TypeScript中处理复杂类型的两种方式。联合类型表示一个值可以是几种类型之一,使用竖线(|)分隔每种类型。交叉类型表示一个值必须满足所有类型,使用和号(&)连接。
// 联合类型
function padLeft(value: string, padding: string | number) {
// ...
}
// 交叉类型
type ReadableWritable = ReadableStream & WritableStream;
问题6: 解释TypeScript中的never类型有什么用途?
在TypeScript中,`never`类型表示的是那些永不存在的值的类型。例如,`never`类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数的返回类型;变量也可能是`never`类型,当它们被永不为真的类型保护所约束时。
function error(message: string): never {
throw new Error(message);
}
问题7: TypeScript中的装饰器是什么?
装饰器是TypeScript的一个实验性特性,它是一种特殊类型的声明,可以被附加到类声明,方法,访问符,属性或参数上。装饰器使用`@expression`这种形式,`expression`求值后必须为一个函数,它会在运行时被调用。
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
问题8: 解释TypeScript中的类型断言。
类型断言是一种告诉编译器“我知道我在做什么”的方式。它不进行特殊的数据检查或重构。类型断言有两种形式。其一是“尖括号”语法,另一个为`as`语法。
let someValue: any = "this is a string";
let strLength: number = (someValue).length;
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
问题9: TypeScript中的命名空间和模块有什么区别?
在TypeScript中,命名空间和模块都是组织代码的方式。命名空间是在全局定义的代码的内部添加一个额外层级的方法,主要用于防止命名冲突。模块是TypeScript的外部模块的简称,侧重于代码和依赖的管理。模块在其自身的作用域里执行,而不是在全局作用域里,这意味着定义在一个模块里的变量、函数、类等在模块外部是不可见的,除非你明确地使用`export`形式之一导出它们。
// 使用命名空间
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
// 使用模块
export interface StringValidator {
isAcceptable(s: string): boolean;
}
问题10: 解释TypeScript中的类型兼容性。
TypeScript中的类型兼容性是基于结构子类型的。结构类型是一种只使用其成员来描述类型的方式。结构上兼容的类型,即使它们的名字不同,也是兼容的。
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
// OK, because of structural typing
p = new Person();
问题11: TypeScript中的`any`、`unknown`和`never`类型有什么区别?
`any`类型允许赋值为任意JavaScript值,是最灵活的类型。`unknown`类型表示任何值,但是对这个值做操作之前需要先进行类型检查。`never`类型表示的是那些永不存在的值的类型,例如,`never`类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数的返回类型。
// any
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
// unknown
let notKnown: unknown = 4;
notKnown = "could be anything";
if (typeof notKnown === "string") {
console.log(notKnown.toUpperCase()); // Type check
}
// never
function error(message: string): never {
throw new Error(message);
}
问题12: 如何在TypeScript中定义枚举成员的值?
在TypeScript中,枚举成员可以被赋予数值(默认从0开始)或字符串值。
enum Direction {
Up = 1,
Down,
Left,
Right,
}
enum Response {
No = "NO",
Yes = "YES",
}
问题13: TypeScript中的`interface`和`type`有什么区别?
`interface`和`type`都可以用来定义对象的形状或函数签名。`interface`更适合定义公共API的形状,可以被实现(implements)或扩展(extends)。`type`可以用于定义类型别名,支持一些`interface`做不到的复杂类型,如联合类型。`type`也不能被扩展或实现。
interface User {
name: string;
age: number;
}
type Animal = {
species: string;
};
// Extending interface
interface Employee extends User {
salary: number;
}
// Union type
type StringOrNumber = string | number;
问题14: 解释TypeScript中的映射类型。
映射类型是一种基于旧类型创建新类型的方式,通过遍历旧类型的属性来实现。这对于创建一个类型的只读版本或将所有属性设置为可选非常有用。
type Readonly = {
readonly [P in keyof T]: T[P];
};
type Partial = {
[P in keyof T]?: T[P];
};
interface User {
id: number;
name: string;
}
type UserPartial = Partial;
type ReadonlyUser = Readonly;
问题15: TypeScript中的装饰器是如何工作的?
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或参数上。装饰器使用`@expression`形式,`expression`求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
问题16: 如何在TypeScript中使用泛型约束?
泛型约束允许你定义一个接口,以确保泛型类型具有特定的结构。通过使用`extends`关键字,你可以指定泛型必须符合的约束条件。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
问题17: TypeScript中的索引签名是什么?
索引签名用于描述那些我们无法提前知道成员名称的类型,但是知道成员值的类型。通过索引签名,你可以定义对象或数组中值的类型。
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
问题18: 解释TypeScript中的条件类型。
条件类型是TypeScript中的一种高级类型,它允许类型根据条件表达式在编译时选择其中之一。条件类型的形式类似于JavaScript中的三元条件运算符。
type IsNumber<T> = T extends number ? "yes" : "no";
type WithNumber = IsNumber<number>; // "yes"
type WithString = IsNumber<string>; // "no"
问题19: TypeScript中的`keyof`关键字有什么用途?
`keyof`操作符接受一个对象类型并产生一个字符串或数字字面量联合,表示该对象类型的所有公钥键。
interface Person {
name: string;
age: number;
}
let personProps: keyof Person; // 'name' | 'age'
问题20: 如何在TypeScript中定义一个类?
在TypeScript中,类是使用`class`关键字定义的。类可以包含构造函数、属性和方法。你还可以使用`public`、`private`、`protected`和`readonly`等访问修饰符来控制成员的访问级别。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
问题21: 解释TypeScript中的可选链(Optional Chaining)。
可选链(Optional Chaining)是TypeScript 3.7中引入的一个特性,允许你在访问一个对象的多层嵌套属性时,不需要显式地验证每一层是否存在。如果访问的属性不存在,表达式短路返回undefined,而不是抛出错误。
interface User {
info?: {
name?: string;
};
}
const user: User = {};
// 不使用可选链
const nameWithoutOptionalChaining = user.info ? user.info.name : undefined;
// 使用可选链
const nameWithOptionalChaining = user.info?.name;
问题22: TypeScript中的显式类型转换如何实现?
在TypeScript中,你可以使用类型断言来实现显式类型转换。类型断言有两种形式:尖括号语法和as语法。
let someValue: any = "this is a string";
// 尖括号语法
let strLength1: number = (someValue).length;
// as语法
let strLength2: number = (someValue as string).length;
问题23: 解释TypeScript中的装饰器工厂。
装饰器工厂是一个函数,它返回一个表达式,该表达式会在运行时被调用来装饰特定的类成员。装饰器工厂提供了一种灵活的方式来自定义装饰器在不同场景下的行为。
function color(value: string) { // 这是装饰器工厂
return function (target) { // 这是装饰器
// do something with 'target' and 'value'...
}
}
@color('blue') // 使用装饰器工厂
class Car {
}
问题24: TypeScript中的索引类型查询(keyof)和索引访问类型(T[K])有什么用?
索引类型查询(keyof)和索引访问类型(T[K])允许你动态地从对象类型中查询和访问属性类型。`keyof T`产生类型T的所有公共键的联合,而`T[K]`则表示对象T的属性K的类型。
interface Person {
name: string;
age: number;
}
type Keys = keyof Person; // 'name' | 'age'
type NameType = Person['name']; // string
问题25: TypeScript中的类型守卫是什么?
类型守卫是一些表达式,它们在运行时检查以确保在某个作用域内变量属于某个特定类型。类型守卫可以通过自定义类型保护函数、`typeof`、`instanceof`或通过使用非空断言等方式实现。
function isNumber(x: any): x is number {
return typeof x === "number";
}
function doSomething(x: number | string) {
if (isNumber(x)) {
console.log(x.toFixed(2)); // x在这里被识别为number类型
} else {
console.log(x.length); // x在这里被识别为string类型
}
}
问题26: 解释TypeScript中的类型映射。
类型映射允许你基于旧类型创建新类型,它通过一个旧类型的每个属性进行迭代,并将其转换成新类型。这是一种强大的类型转换手段,可以用于修改现有类型的属性。
type Readonly = {
readonly [P in keyof T]: T[P];
};
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly;
问题27: TypeScript中的类型推断。
TypeScript的类型推断允许你在不显式指定类型的情况下,让编译器自动推断出变量或函数返回值的类型。这减少了类型声明的需要,但仍保持了类型安全。
let x = 3; // x被推断为number
const arr = [0, 1, null]; // arr被推断为(number | null)[]
问题28: 解释TypeScript中的类型守卫和区分类型。
类型守卫是运行时检查某个变量类型的表达式,而区分类型(Discriminated Unions)是一种模式,它涉及到具有共同的字面量属性的类型联合。这两种特性通常结合使用,以便在类型较为复杂的情况下安全地区分和使用联合类型。
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}
问题29: TypeScript中的装饰器元数据。
装饰器元数据是TypeScript的一个高级特性,它允许在装饰器中附加和使用类型信息。这需要在tsconfig.json中启用`experimentalDecorators`和`emitDecoratorMetadata`选项。
import "reflect-metadata";
function logType(target: any, key: string) {
var t = Reflect.getMetadata("design:type", target, key);
console.log(`${key} type: ${t.name}`);
}
class Demo {
@logType // 应用装饰器
public attr1: string;
}
问题30: TypeScript中的命名空间与模块的区别。
命名空间和模块都是TypeScript中组织和封装代码的方式。命名空间主要用于组织全局代码,避免命名冲突。模块则是ES6引入的,用于封装和重用代码,依赖于导入和导出来共享模块之间的代码。
// 使用命名空间
namespace MyNamespace {
export class MyClass { }
}
// 使用模块
export class MyModuleClass { }
问题31: 如何在TypeScript中使用装饰器来实现单例模式?
装饰器可以用来修改类的行为,以下是一个使用类装饰器实现单例模式的示例。单例模式确保一个类只有一个实例,并提供一个全局访问点。
function Singleton(constructor:T) {
const instanceKey = Symbol.for("instance");
return class extends constructor {
constructor(...args: any[]) {
super(...args);
if (!Reflect.has(constructor, instanceKey)) {
Reflect.set(constructor, instanceKey, this);
}
return Reflect.get(constructor, instanceKey);
}
}
}
@Singleton
class Example {
constructor(public name: string) {}
}
const a = new Example("a");
const b = new Example("b");
console.log(a === b); // true
console.log(a.name); // a
问题32: TypeScript中的`Partial`和`Required`工具类型有什么用途?
`Partial`和`Required`是TypeScript中的内置工具类型,用于修改类型的属性。`Partial
interface MyInterface {
a: number;
b: string;
}
const partial: Partial = {}; // a和b都是可选的
const required: Required = {a: 1, b: "string"}; // a和b都是必需的
问题33: 解释TypeScript中的`Readonly`和`ReadonlyArray`工具类型。
`Readonly`和`ReadonlyArray`是TypeScript中的内置工具类型,用于创建不可修改的类型和数组。`Readonly
interface MyInterface {
a: number;
}
const readonlyObj: Readonly = {a: 1};
// readonlyObj.a = 2; // Error: Cannot assign to 'a' because it is a read-only property.
const readonlyArray: ReadonlyArray = [1, 2, 3];
// readonlyArray[0] = 4; // Error: Index signature in type 'readonly number[]' only permits reading.
问题34: 如何在TypeScript中使用`Exclude`和`Extract`工具类型?
`Exclude`和`Extract`是TypeScript中的内置工具类型,用于根据类型之间的关系进行条件选择。`Exclude
type T1 = string | number | boolean;
type T2 = Exclude; // string
type T3 = Extract; // number | boolean
问题35: TypeScript中的`Omit`工具类型是用来做什么的?
`Omit
interface MyInterface {
a: number;
b: string;
c: boolean;
}
type Omitted = Omit; // { c: boolean; }
问题36: 在TypeScript中如何使用`Record`工具类型?
`Record
type Page = "home" | "about" | "contact";
const pageData: Record = {
home: { title: "Home Page" },
about: { title: "About Us" },
contact: { title: "Contact Us" }
};
问题37: 解释TypeScript中的`Pick`工具类型。
`Pick
interface Todo {
id: number;
title: string;
completed: boolean;
}
type TodoPreview = Pick;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};
问题38: TypeScript中的`ReturnType`工具类型有什么用途?
`ReturnType
type T0 = ReturnType<() => string>; // string
function f1(s: string) {
return {a: 1, b: s};
}
type T1 = ReturnType; // { a: number, b: string }
问题39: 如何在TypeScript中定义自定义类型守卫?
自定义类型守卫是一种使用类型谓词`arg is T`来检查类型的函数。这允许你在运行时检查类型,并且在这个函数的作用域内,TypeScript编译器将能够识别类型。
function isString(test: any): test is string {
return typeof test === "string";
}
function example(foo: any) {
if (isString(foo)) {
// foo在这个作用域内被识别为string类型
console.log(foo.length); // 正确
}
}
问题40: TypeScript中的`infer`关键字有什么用途?
`infer`关键字在条件类型中使用,允许你在条件类型的`extends`子句中推断类型。这对于在条件类型中需要引用某种类型的情况非常有用。
type UnpackPromise = T extends Promise ? U : T;
type T0 = UnpackPromise>; // string
type T1 = UnpackPromise; // string
问题41: TypeScript中的`unknown`类型与`any`类型有什么区别?
`unknown`类型是TypeScript 3.0中引入的顶级类型,它代表任何值,但与`any`不同,对`unknown`类型的值进行操作之前,需要先进行类型检查。这提供了比`any`更严格的类型安全。
let value: unknown;
// 错误: 对象的类型为 "unknown"
// value.toUpperCase();
if (typeof value === "string") {
// 正确: 在这个块中, value的类型被缩小为 "string"
console.log(value.toUpperCase());
}
问题42: 如何在TypeScript中使用`const`断言?
`const`断言是TypeScript 3.4中引入的一种特殊类型断言,用于告诉编译器处理为最字面量的类型,而不是更宽泛的类型。这对于确保对象或数组不被改变非常有用。
let x = "hello" as const; // x的类型为 "hello"
let y = [10, 20] as const; // y的类型为 readonly [10, 20]
问题43: TypeScript中的`this`类型有什么用途?
`this`类型表示的是某个包含类或接口的类型。它主要用于链式调用中,确保方法正确地返回它们所属的类型。
class BasicCalculator {
public constructor(protected value: number = 0) { }
public currentValue(): number {
return this.value;
}
public add(operand: number): this {
this.value += operand;
return this;
}
// 其他方法...
}
class ScientificCalculator extends BasicCalculator {
public constructor(value = 0) {
super(value);
}
public sin() {
this.value = Math.sin(this.value);
return this;
}
// 其他方法...
}
问题44: 解释TypeScript中的类型谓词。
类型谓词用于自定义类型守卫函数。通过在函数返回类型位置使用`parameterName is Type`这样的语法,你可以告诉TypeScript编译器参数是否为特定的类型。
function isNumber(x: any): x is number {
return typeof x === "number";
}
function doSomething(value: any) {
if (isNumber(value)) {
console.log(value.toFixed(2)); // 编译器知道value是number类型
} else {
console.log(value); // value在这里是any类型
}
}
问题45: TypeScript中的`as const`与普通的`const`有什么区别?
`as const`是TypeScript中的一个类型断言,用于将字面量类型断言为最具体的(不可变的)类型。与普通的`const`(用于声明一个不可变的变量)不同,`as const`影响的是类型系统层面,使得对象属性成为只读,数组成为只读数组。
const a = { x: 1 }; // a的类型是 { x: number }
const b = { x: 1 } as const; // b的类型是 { readonly x: 1 }
const arr1 = [1, 2, 3]; // arr1的类型是 number[]
const arr2 = [1, 2, 3] as const; // arr2的类型是 readonly [1, 2, 3]
问题46: TypeScript中的`unknown`类型如何用于提高代码的安全性?
`unknown`类型是TypeScript中的一种类型安全的顶级类型,用于表示任何值。与`any`类型不同,`unknown`类型的值在进行操作之前需要进行类型检查或类型断言,这样可以避免在编译时不被发现的运行时错误。
function safelyParse(jsonString: string): unknown {
return JSON.parse(jsonString);
}
const value: unknown = safelyParse(`{"name": "Alice"}`);
if (typeof value === "object" && value !== null && "name" in value) {
console.log(value.name); // Error: Object is of type 'unknown'.
console.log((value as { name: string }).name); // 正确: 使用类型断言
}
问题47: 在TypeScript中如何使用类型守卫来缩小类型范围?
类型守卫是TypeScript中的一种特性,允许你通过检查类型信息来缩小变量的类型范围。常见的类型守卫包括`typeof`检查、`instanceof`检查、用户自定义的类型守卫以及使用`in`关键字检查。
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
if ("swim" in pet) {
pet.swim();
} else {
pet.fly();
}
问题48: 解释TypeScript中的映射修改器`readonly`和`?`的作用。
在TypeScript中,映射修改器`readonly`和`?`可以用于映射类型中,分别用于使属性变为只读和可选。
type Readonly = {
readonly [P in keyof T]: T[P];
};
type Partial = {
[P in keyof T]?: T[P];
};
interface User {
id: number;
name: string;
}
type ReadonlyUser = Readonly;
type PartialUser = Partial;
问题49: TypeScript中的泛型约束有什么用途?举例说明。
泛型约束允许在TypeScript中对泛型进行限制,确保泛型符合特定的接口或类型。这在需要对泛型参数执行特定操作时非常有用,因为它提供了类型信息。
interface Lengthwise {
length: number;
}
function loggingIdentity(arg: T): T {
console.log(arg.length); // 现在我们知道它具有.length属性
return arg;
}
loggingIdentity({length: 10, value: 3});
问题50: 如何在TypeScript中使用模块解析?
TypeScript中的模块解析是指编译器查找导入模块的过程。TypeScript提供了相对和非相对模块导入。配置项`moduleResolution`可以用来指定使用`node`还是`classic`模式来解析模块。
// 相对导入
import { myFunction } from "./myModule";
// 非相对导入
import { myFunction } from "my-module";
问题51: 解释TypeScript中的装饰器元数据反射。
装饰器元数据反射是TypeScript的一个高级特性,它允许在运行时查询有关类、方法和属性的类型信息。这需要在tsconfig.json中启用`experimentalDecorators`和`emitDecoratorMetadata`选项,并且需要引入`reflect-metadata`库。
import "reflect-metadata";
function logType(target: any, key: string) {
var t = Reflect.getMetadata("design:type", target, key);
console.log(`${key} type: ${t.name}`);
}
class MyClass {
@logType // 应用装饰器
public attr1: string;
}
问题52: TypeScript中的`declare`关键字有什么用途?
`declare`关键字在TypeScript中用于声明变量、类、函数等的类型,而不实现它们。它通常用于描述非TypeScript编写的代码的类型信息,如声明JavaScript库的类型。
declare const myLibraryFunction: (input: string) => void;
// 现在可以安全地使用这个函数,TypeScript会检查类型
myLibraryFunction("Hello, world!");
问题53: 如何在TypeScript中使用命名空间和模块?
命名空间和模块都是组织和封装代码的方式。命名空间主要用于组织全局代码,避免命名冲突。模块则是基于文件的,每个模块对应一个文件,依赖于导入和导出来共享代码。
// 使用命名空间
namespace MyNamespace {
export class MyClass { }
}
// 使用模块
// file: myModule.ts
export class MyModuleClass { }
// 在另一个文件中导入
import { MyModuleClass } from "./myModule";
问题54: TypeScript中的`type`和`interface`有什么区别?
`type`和`interface`都可以用来定义对象的形状或函数签名。`interface`更适合定义公共API的形状,可以被实现(implements)或扩展(extends)。`type`可以用于定义类型别名,支持一些`interface`做不到的复杂类型,如联合类型。`type`也不能被扩展或实现。
interface User {
name: string;
age: number;
}
type Animal = {
species: string;
};
// Extending interface
interface Employee extends User {
salary: number;
}
// Union type
type StringOrNumber = string | number;
问题55: 在TypeScript中如何使用泛型约束?
泛型约束允许你在TypeScript中对泛型进行限制,确保泛型符合特定的接口或类型。这在需要对泛型参数执行特定操作时非常有用,因为它提供了类型信息。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们知道它具有.length属性
return arg;
}
loggingIdentity({length: 10, value: 3});
问题56: 解释TypeScript中的索引类型和如何使用它们。
索引类型允许我们使用动态属性名。通过索引类型,我们可以查询属性的类型,并确保代码不会引用不存在的属性名。
function pluck(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
问题57: TypeScript中的`keyof`和`typeof`操作符有什么用途?
`keyof`操作符可以用来取得一个对象接口的所有键的联合类型。`typeof`操作符可以用来获取一个变量声明或对象的类型。
interface Person {
name: string;
age: number;
}
let personProps: keyof Person; // 'name' | 'age'
let person = { name: 'Alice', age: 25 };
type PersonType = typeof person; // { name: string; age: number; }
问题58: 如何在TypeScript中定义和使用枚举(Enum)?
枚举(Enum)是TypeScript为JavaScript扩展的一种数据类型,它允许为一组数值赋予友好的名字。
enum Color {
Red,
Green,
Blue,
}
let c: Color = Color.Green;
console.log(c); // 输出 1
问题59: TypeScript中的交叉类型是什么,如何使用?
交叉类型是将多个类型合并为一个类型,这让我们可以将现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。
interface UseWithWater {
water(): void;
}
interface UseWithFire {
fire(): void;
}
type Wizard = UseWithWater & UseWithFire;
let wizard: Wizard = {
water: () => console.log('Water!'),
fire: () => console.log('Fire!')
};
问题60: TypeScript中的`never`类型通常用于哪些场景?
`never`类型表示的是那些永不存在的值的类型。它通常用于不可能返回值的函数的返回类型,或用于总是会抛出异常的函数。
function error(message: string): never {
throw new Error(message);
}
function fail() {
return error("Something failed");
}
function infiniteLoop(): never {
while (true) {}
}
问题61: TypeScript中的`readonly`修饰符有什么作用?
`readonly`修饰符用于属性声明或索引签名,表示该属性或索引不可被重新赋值。这对于定义不可变数据非常有用。
interface Point {
readonly x: number;
readonly y: number;
}
let point: Point = { x: 10, y: 20 };
// point.x = 5; // Error: Cannot assign to 'x' because it is a read-only property.
问题62: 如何在TypeScript中使用`abstract`类和方法?
`abstract`关键字用于定义抽象类和在抽象类内部定义抽象方法。抽象类不能被实例化,通常作为其他类的基类使用。抽象方法只能在抽象类内部声明,并且必须在派生类中实现。
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earth...');
}
}
class Dog extends Animal {
makeSound() {
console.log('Woof! Woof!');
}
}
// let animal = new Animal(); // Error: Cannot create an instance of an abstract class.
let dog = new Dog();
dog.makeSound(); // Outputs: 'Woof! Woof!'
dog.move(); // Outputs: 'roaming the earth...'
问题63: 解释TypeScript中的类型推断。
TypeScript的类型推断允许不显式指定变量的类型,而是让TypeScript编译器自动推断出最合适的类型。这减少了冗余代码,同时保持了类型安全。
let x = 3; // 类型推断为number
let y = [0, 1, null]; // 类型推断为(number | null)[]
问题64: TypeScript中的`enum`如何使用,以及如何反向映射?
TypeScript的`enum`允许为一组数值定义友好的名称。`enum`类型默认从0开始为元素编号,但你可以手动设置成员的值。TypeScript的`enum`是双向映射的,即可以通过枚举值找到对应的名称。
enum Color {
Red = 1,
Green,
Blue
}
let colorName: string = Color[2];
console.log(colorName); // 显示'Green',因为上面代码中它的值是2
问题65: TypeScript中的`Tuple`类型是什么,以及如何使用?
`Tuple`类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。可以对元素进行类型注解。
let x: [string, number];
x = ["hello", 10]; // OK
// x = [10, "hello"]; // Error, 类型不匹配
问题66: 解释TypeScript中的联合类型和交叉类型。
联合类型表示一个值可以是几种类型之一,使用竖线`|`分隔每种类型。交叉类型表示一个值必须满足所有类型,使用`&`连接。
// 联合类型
function padLeft(value: string, padding: string | number) {
// ...
}
// 交叉类型
type ReadableWritable = ReadableStream & WritableStream;
问题67: TypeScript中如何使用类型别名?
类型别名用于创建一个类型的新名称。类型别名可以是基本类型、联合类型、元组甚至是任何其他类型。使用`type`关键字定义。
type StringOrNumber = string | number;
type Text = string | { text: string };
type Coordinates = [number, number];
type Callback = () => void;
问题68: TypeScript中的`type`和`interface`有何异同?
`type`和`interface`都可以用来定义对象的形状或函数签名。`interface`更适合声明对象的形状,可以被扩展和实现。`type`更灵活,可以用于声明联合类型、元组等,但不能被扩展或实现。
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
type StringOrNumber = string | number;
type AnimalType = {
name: string;
};
// Error: An interface cannot extend a type alias.
// interface BearType extends AnimalType {
// honey: boolean;
// }
问题69: 如何在TypeScript中声明全局变量?
在TypeScript中,全局变量可以通过在`.d.ts`文件中使用`declare`关键字来声明。这样的声明不会创建一个新的变量,而是告诉TypeScript该变量已经存在。
// 在 some.d.ts 文件中
declare var myGlobalVar: any;
// 现在可以在任何地方安全地使用 myGlobalVar,无需再次声明
console.log(myGlobalVar);
问题70: TypeScript中的`as`关键字是做什么用的?
`as`关键字在TypeScript中用于类型断言,允许你将某个值视为任意类型。类型断言不会进行类型转换,它只是在编译阶段告诉编译器如何理解该值。
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
问题71: TypeScript中的`unknown`类型与`any`类型的主要区别是什么?
`unknown`类型是TypeScript 3.0引入的类型安全的顶级类型,它代表任何值,但与`any`不同,对`unknown`类型的值进行操作之前,需要进行类型检查或类型断言。这样做的目的是为了增强代码的类型安全性,避免类型错误。
let value: unknown;
// 直接操作value会出错
// value.toUpperCase(); // Error
// 类型检查后操作
if (typeof value === "string") {
console.log(value.toUpperCase()); // 正确
}
问题72: 如何在TypeScript中定义一个只读数组?
在TypeScript中,可以使用`ReadonlyArray
let a: ReadonlyArray = [1, 2, 3];
// a.push(4); // Error: Property 'push' does not exist on type 'readonly number[]'.
let b: readonly string[] = ["a", "b", "c"];
// b[0] = "d"; // Error: Index signature in type 'readonly string[]' only permits reading.
问题73: TypeScript中的泛型默认类型是什么?
在TypeScript中,泛型可以有默认类型,当没有明确指定泛型类型时,将使用默认类型。
interface MyInterface {
value: T;
}
let a: MyInterface = { value: "Hello" }; // T默认为string
let b: MyInterface = { value: 10 }; // 明确指定T为number
问题74: 解释TypeScript中的条件类型。
条件类型是TypeScript中的一种高级类型,它允许类型根据条件表达式在编译时选择其中之一。条件类型的形式类似于JavaScript中的三元条件运算符。
type IsString = T extends string ? true : false;
type A = IsString; // true
type B = IsString; // false
问题75: TypeScript中的`Exclude`和`Extract`工具类型有什么用途?
`Exclude`和`Extract`是TypeScript中的内置工具类型,用于根据类型之间的关系进行条件选择。`Exclude
type T1 = string | number | boolean;
type T2 = Exclude; // string
type T3 = Extract; // number | boolean
问题76: 在TypeScript中,如何使用`never`类型来表示不可达代码?
`never`类型表示的值永不存在,它通常用于函数的返回类型,表示函数永远不会正常结束(总是抛出错误或无限循环)。因此,`never`类型可用于标记不可达代码的位置。
function throwError(errorMsg: string): never {
throw new Error(errorMsg);
}
function infiniteLoop(): never {
while (true) {}
}
// 使用never类型的变量
function check(x: string | number): boolean {
if (typeof x === "string") {
return true;
} else if (typeof x === "number") {
return false;
}
// x在这里是never类型
const exhaustiveCheck: never = x;
return exhaustiveCheck;
}
问题77: TypeScript中的`const`断言有什么作用?
`const`断言是TypeScript中的一种特殊断言,用于告诉编译器处理字面量对象或数组为只读,并且对象的所有属性都是确切的,不允许添加或删除属性。这对于确保对象或数组在编译时保持不变非常有用。
let x = "hello" as const; // x的类型为 "hello"
let y = [10, 20] as const; // y的类型为 readonly [10, 20]
const obj = {
name: "Alice",
age: 30
} as const; // obj的类型为 { readonly name: "Alice"; readonly age: 30; }
问题78: 如何在TypeScript中重载函数?
在TypeScript中,函数重载允许创建多个同名函数,但参数列表不同。这使得可以根据不同的参数类型或数量执行不同的操作。
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
return a + b;
}
const sum = add(1, 2); // 返回number
const concat = add("hello", "world"); // 返回string
问题79: TypeScript中的`this`类型有什么特殊之处?
TypeScript中的`this`类型表示方法的调用者类型,它在对象方法中特别有用,可以确保方法链式调用时`this`的类型正确。
class Counter {
private count = 0;
public add(value: number) {
this.count += value;
return this;
}
public subtract(value: number) {
this.count -= value;
return this;
}
public log() {
console.log(this.count);
return this;
}
}
const counter = new Counter();
counter.add(5).subtract(2).log(); // 链式调用
问题80: 解释TypeScript中的类型兼容性。
TypeScript的类型兼容性是基于结构子类型的。这意味着如果一个类型的所有成员的类型都是兼容的,那么这个类型就被认为是兼容的。这种方式使得在不同类型之间的赋值变得灵活。
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
// OK, 因为Person有一个name属性,满足Named接口的要求
p = new Person();
问题81: 解释TypeScript中的映射类型。
映射类型允许从旧类型中创建新类型,通过遍历旧类型的属性键来实现。这种类型非常灵活,可以用于创建只读类型、可选类型等。
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
// 等价于
type Flags = {
option1: boolean;
option2: boolean;
}
问题82: TypeScript中的类型保护是什么?
类型保护是TypeScript中的一种技术,用于确保变量属于某个特定类型。类型保护可以通过自定义类型保护函数、`typeof`关键字、`instanceof`关键字或`in`关键字实现。
function isString(test: any): test is string {
return typeof test === "string";
}
function example(foo: any) {
if (isString(foo)) {
console.log(foo.length); // foo在这里被识别为string类型
}
}
问题83: 如何在TypeScript中使用泛型约束?
泛型约束允许你限制泛型类型必须符合的接口或类型。这在需要对泛型参数执行特定操作时非常有用,因为它提供了类型信息。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
问题84: TypeScript中的`Partial`、`Required`、`Readonly`、`Pick`和`Record`工具类型分别是用来做什么的?
这些都是TypeScript中的内置工具类型,用于转换现有类型为新类型:
- `Partial
`:使类型`T`的所有属性变为可选。 - `Required
`:使类型`T`的所有属性变为必需。 - `Readonly
`:使类型`T`的所有属性变为只读。 - `Pick
`:从类型`T`中选择一组属性`K`来构造类型。 - `Record
`:创建一个对象类型,其属性键为`K`,属性值为`T`。
// 示例
type Todo = {
id: number;
text: string;
done: boolean;
};
type TodoPreview = Pick<Todo, 'id' | 'text'>;
type ReadonlyTodo = Readonly<Todo>;
问题85: TypeScript中的`unknown`类型与`any`类型相比,为什么更受欢迎?
`unknown`类型是TypeScript中的一种类型安全的顶级类型,与`any`类型相比,`unknown`类型在编译时不允许对其值进行任意操作,除非类型被明确地检查或断言。这增加了代码的类型安全性。
let value: unknown;
// 直接操作value会出错
// value.toUpperCase(); // Error
// 类型检查后操作
if (typeof value === "string") {
console.log(value.toUpperCase()); // 正确
}
问题86: 解释TypeScript中的`infer`关键字。
`infer`关键字在TypeScript的条件类型中使用,允许在条件类型的`extends`子句中推断类型。这使得条件类型可以更灵活地提取和使用类型信息。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
// 使用ReturnType推断函数返回类型
function getString() {
return "Hello, TypeScript";
}
type Str = ReturnType<typeof getString>; // Str被推断为string
问题87: TypeScript中的`keyof`类型操作符有什么用途?
`keyof`类型操作符用于获取某种类型的所有键的联合类型。这对于确保对对象键的引用是类型安全的非常有用。
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person; // "name" | "age"
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const person: Person = {
name: "Alice",
age: 30
};
const name: string = getProperty(person, "name");
问题89: TypeScript中的类型断言有哪些形式?
在TypeScript中,类型断言有两种形式:尖括号语法和`as`语法。类型断言允许你告诉编译器你知道某个值的更具体的类型。
let someValue: any = "this is a string";
// 尖括号语法
let strLength1: number = (someValue).length;
// as语法
let strLength2: number = (someValue as string).length;
问题90: 解释TypeScript中的类型守卫`is`。
在TypeScript中,`is`用于自定义类型守卫。通过在函数返回类型位置使用`parameterName is Type`这样的语法,你可以告诉TypeScript编译器参数是否为特定的类型。
function isString(test: any): test is string {
return typeof test === "string";
}
function doSomething(value: any) {
if (isString(value)) {
console.log(value.length); // 编译器知道value是string类型
}
}
问题91: TypeScript中的`unknown`类型如何与类型守卫结合使用?
`unknown`类型是TypeScript中的一种类型安全的顶级类型,它代表任何值。与`any`类型不同,对`unknown`类型的值进行操作之前,需要进行类型检查或类型断言。类型守卫可以用来缩小`unknown`类型的范围,使其在特定区块中被识别为更具体的类型。
function doSomething(value: unknown) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // 类型守卫缩小为string
} else if (Array.isArray(value)) {
console.log(value.length); // 类型守卫缩小为any[]
}
}
问题92: 如何在TypeScript中定义和使用异步函数?
异步函数在TypeScript中通过`async`和`await`关键字定义和使用。`async`关键字用于声明一个异步函数,而`await`关键字用于等待一个`Promise`的结果。这使得异步代码的编写更加直观和易于理解。
async function fetchData(url: string): Promise {
const response = await fetch(url);
const data = await response.text();
return data;
}
fetchData('https://example.com').then(data => {
console.log(data);
});
问题93: TypeScript中的`readonly`和`const`有什么区别?
`readonly`和`const`都用于声明不可变的变量,但它们的应用范围不同。`readonly`用于类的属性或接口的字段,表示该属性或字段在初始化后不可更改。`const`用于变量声明,表示该变量的值不可重新赋值。
class Example {
readonly prop: string;
constructor(prop: string) {
this.prop = prop;
}
}
const CONST_VALUE = "constant";
// CONST_VALUE = "new value"; // Error: Left-hand side of assignment expression cannot be a constant or a read-only property.
问题94: 解释TypeScript中的`Pick`和`Omit`工具类型。
`Pick`和`Omit`是TypeScript中的内置工具类型,用于根据现有类型创建新类型。`Pick
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick;
type TodoWithoutDescription = Omit;
问题95: TypeScript中的`Partial`工具类型是如何工作的?
`Partial
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial) {
return { ...todo, ...fieldsToUpdate };
}
const todo1: Todo = {
title: "organize desk",
description: "clear clutter",
};
const todo2 = updateTodo(todo1, {
description: "throw out trash",
});
问题96: 在TypeScript中如何使用`Exclude`工具类型来排除联合类型中的某些类型?
`Exclude`工具类型从联合类型中排除指定的类型,创建一个新的类型。这对于从现有类型中移除不需要的类型非常有用。
type T0 = string | number | boolean;
type T1 = Exclude<T0, string>; // number | boolean
// 使用Exclude排除类型
function filterOutStrings(value: T0): T1 {
if (typeof value === "string") {
throw new Error("Function does not accept strings.");
}
return value;
}
问题97: TypeScript中`ReadonlyArray`与普通数组有什么区别?
`ReadonlyArray`类型表示一个只读数组,不能对其进行修改操作(如添加、删除元素),保证数组内容的不变性。普通数组没有这些限制。
let list: number[] = [1, 2, 3];
list.push(4); // 正常操作
list[0] = 10; // 正常操作
let roList: ReadonlyArray<number> = [1, 2, 3];
// roList.push(4); // Error: Property 'push' does not exist on type 'readonly number[]'.
// roList[0] = 10; // Error: Index signature in type 'readonly number[]' only permits reading.
问题98: 如何在TypeScript中定义具有可调用签名的对象?
在TypeScript中,可以通过在接口或类型别名中定义一个调用签名来创建具有可调用签名的对象。
interface AddFunction {
(a: number, b: number): number;
}
const add: AddFunction = function(a, b) {
return a + b;
};
console.log(add(10, 20)); // 30
问题99: 解释TypeScript中的`Record`工具类型及其用途。
`Record
type Page = "home" | "about" | "contact";
const pageInfo: Record<Page, { title: string }> = {
home: { title: "Home" },
about: { title: "About Us" },
contact: { title: "Contact" }
};
问题100: TypeScript中的类型断言与类型守卫有什么区别?
类型断言是一种语法,允许开发者告诉编译器“我知道这个变量的类型”,而不进行运行时检查。类型守卫则是一种运行时检查,它根据某些条件(如`typeof`、`instanceof`或自定义类型守卫函数)缩小变量的类型范围。
// 类型断言
const someValue: any = "this is a string";
const strLength: number = (someValue as string).length;
// 类型守卫
function isNumber(value: any): value is number {
return typeof value === "number";
}
if (isNumber(someValue)) {
console.log(someValue.toFixed(2)); // someValue在这里被识别为number类型
}
问题102: 在TypeScript中如何定义一个只读的对象类型?
在TypeScript中,可以使用`Readonly`工具类型或`readonly`关键字来定义一个只读的对象类型,这样的对象一旦被创建,其属性就不能被修改。
interface User {
readonly id: number;
readonly name: string;
}
// 或者使用Readonly工具类型
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = { id: 1, name: "John" };
// user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.
问题103: 解释TypeScript中的类型推断规则。
TypeScript的类型推断允许不显式指定变量的类型,而是让TypeScript编译器自动推断出最合适的类型。类型推断发生在初始化变量、设置默认参数值和决定函数返回值时。TypeScript会根据右侧的值来推断左侧变量的类型。
let x = 3; // x被推断为number
const arr = [0, 1, null]; // arr被推断为(number | null)[]
function add(a = 0) { // 参数a被推断为number
return a + 10;
}
问题105: 解释TypeScript中的装饰器模式及其用法。
装饰器是TypeScript的一个实验性特性,它是一种特殊类型的声明,可以被附加到类声明、方法、访问符、属性或参数上。装饰器使用`@expression`这种形式,`expression`求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。装饰器用于修改、监视、替换类成员的定义。
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
问题106: TypeScript中的`as const`有什么作用?
`as const`用于将字面量表达式转换为具有最具体字面类型的常量,从而使得对象属性成为只读,并且数组成为元组类型。
const obj = { x: 10, y: 20 } as const;
// obj的类型自动成为 { readonly x: 10; readonly y: 20; }
const arr = [1, 2, 3] as const;
// arr的类型自动成为 readonly [1, 2, 3]
问题107: 如何在TypeScript中使用索引签名?
索引签名用于描述那些我们无法提前知道成员名称的类型,但是知道成员值的类型。通过索引签名,你可以定义对象或数组中值的类型。
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
问题108: TypeScript中的泛型约束有什么用途?
泛型约束允许在TypeScript中对泛型进行限制,确保泛型符合特定的接口或类型。这在需要对泛型参数执行特定操作时非常有用,因为它提供了类型信息。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们知道它具有.length属性
return arg;
}
问题109: 解释TypeScript中的条件类型。
条件类型是TypeScript中的一种高级类型,它允许类型根据条件表达式在编译时选择其中之一。条件类型的形式类似于JavaScript中的三元条件运算符。
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
问题111: TypeScript中的`unknown`类型如何用于增强函数的类型安全?
`unknown`类型是TypeScript中的一种类型安全的顶级类型,用于表示任何值。与`any`类型相比,使用`unknown`类型可以强制开发者在对变量进行操作之前进行类型检查,从而增强函数的类型安全。
function safeParse(s: string): unknown {
return JSON.parse(s);
}
const obj = safeParse('{"name":"Alice"}');
if (typeof obj === "object" && obj !== null && "name" in obj) {
console.log(obj.name); // 类型安全地访问name属性
}
问题112: 如何在TypeScript中使用类型守卫来缩小类型范围?
类型守卫是TypeScript中的一种特性,允许通过某些检查来缩小变量的类型范围。常见的类型守卫包括`typeof`检查、`instanceof`检查、用户自定义的类型守卫以及使用`in`关键字检查。
class Bird {
fly() {
console.log("bird flies");
}
}
class Fish {
swim() {
console.log("fish swims");
}
}
function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}
问题113: 在TypeScript中如何声明一个全局变量?
在TypeScript中,全局变量可以通过在`.d.ts`文件中使用`declare`关键字来声明。这样的声明不会创建一个新的变量,而是告诉TypeScript该变量已经存在。
// 在 some-global.d.ts 文件中
declare var myGlobalVar: any;
// 现在可以在任何地方安全地使用 myGlobalVar,无需再次声明
console.log(myGlobalVar);
问题117: 如何在TypeScript中使用枚举成员的数值和字符串值?
在TypeScript中,枚举成员可以被赋予数值(默认从0开始)或字符串值。数值枚举支持反向映射,而字符串枚举不支持。
enum Color {
Red = 1,
Green,
Blue,
}
enum Message {
Greeting = "Hello",
Farewell = "Goodbye",
}
console.log(Color.Red); // 输出 1
console.log(Color[1]); // 输出 "Red",数值枚举的反向映射
console.log(Message.Greeting); // 输出 "Hello"
问题118: TypeScript中的`ReadonlyArray`与普通数组有什么区别?
`ReadonlyArray`类型表示一个只读数组,不能对其进行修改操作(如添加、删除元素),保证数组内容的不变性。普通数组没有这些限制。
let list: number[] = [1, 2, 3];
list.push(4); // 正常操作
list[0] = 10; // 正常操作
let roList: ReadonlyArray<number> = [1, 2, 3];
// roList.push(4); // Error: Property 'push' does not exist on type 'readonly number[]'.
// roList[0] = 10; // Error: Index signature in type 'readonly number[]' only permits reading.
问题119: 在TypeScript中如何声明一个只读的对象类型?
在TypeScript中,可以使用`Readonly`工具类型或`readonly`关键字来定义一个只读的对象类型,这样的对象一旦被创建,其属性就不能被修改。
interface User {
readonly id: number;
readonly name: string;
}
// 或者使用Readonly工具类型
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = { id: 1, name: "John" };
// user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.
问题122: 在TypeScript中如何定义和使用异步函数?
异步函数在TypeScript中通过`async`和`await`关键字定义和使用。`async`关键字用于声明一个异步函数,而`await`关键字用于等待一个`Promise`的结果。这使得异步代码的编写更加直观和易于理解。
async function fetchData(url: string): Promise {
const response = await fetch(url);
const data = await response.text();
return data;
}
fetchData('https://example.com').then(data => {
console.log(data);
});