TypeScript 常用知识点及面试问题
TypeScript 基础知识点
1. TypeScript 简介
- TypeScript 是 JavaScript 的超集,添加了静态类型系统
- 由微软开发,最终编译成 JavaScript
- 支持所有 JavaScript 特性,同时提供类型检查
- 提供更好的开发体验、代码可维护性和错误检测
2. 基本类型
原始类型
let name: string = "TypeScript";
let age: number = 25;
let pi: number = 3.14;
let hex: number = 0xf00d;
let isDone: boolean = false;
let nothing: null = null;
let notDefined: undefined = undefined;
let sym: symbol = Symbol("key");
let big: bigint = 100n;
数组类型
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];
let readonlyArray: ReadonlyArray<number> = [1, 2, 3];
元组类型
let tuple: [string, number] = ["hello", 42];
let optionalTuple: [string, number?] = ["hello"];
let restTuple: [string, ...number[]] = ["hello", 1, 2, 3];
枚举类型
enum Direction {
Up = 1,
Down,
Left,
Right,
}
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
const enum Status {
Active,
Inactive,
}
any 和 unknown
let anything: any = 42;
anything = "hello";
anything.method();
let unknownValue: unknown = 42;
if (typeof unknownValue === "string") {
console.log(unknownValue.toUpperCase());
}
void 和 never
function log(message: string): void {
console.log(message);
}
function error(message: string): never {
throw new Error(message);
}
3. 对象类型
接口
interface Person {
readonly id: number;
name: string;
age?: number;
[key: string]: any;
}
interface Employee extends Person {
department: string;
}
interface SearchFunc {
(source: string, subString: string): boolean;
}
类型别名
type ID = number | string;
type User = {
id: ID;
name: string;
email: string;
};
type Status = "active" | "inactive" | "pending";
type AdminUser = User & {
isAdmin: true;
permissions: string[];
};
4. 函数类型
函数声明
function add(x: number, y: number): number {
return x + y;
}
const multiply = (x: number, y: number): number => x * y;
function greet(name: string, greeting?: string): string {
return greeting ? `${greeting}, ${name}` : `Hello, ${name}`;
}
function createPerson(name: string, age: number = 18): Person {
return { name, age };
}
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
function process(input: string): string;
function process(input: number): number;
function process(input: string | number): string | number {
return typeof input === "string" ? input.toUpperCase() : input * 2;
}
5. 高级类型
泛型
function identity<T>(arg: T): T {
return arg;
}
interface Box<T> {
value: T;
}
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
条件类型
type IsArray<T> = T extends any[] ? true : false;
type NonNullable<T> = T extends null | undefined ? never : T;
type ExtractType<T, U> = T extends U ? T : never;
映射类型
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
类型推断
let x = 3;
let value: any = "hello world";
let length: number = (value as string).length;
let length2: number = (<string>value).length;
function printName(name: string | null) {
console.log(name!.toUpperCase());
}
function isString(value: unknown): value is string {
return typeof value === "string";
}
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
class Bird {
fly() {
console.log("flying");
}
}
class Fish {
swim() {
console.log("swimming");
}
}
function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly();
} else {
animal.swim();
}
}
6. 类与接口
类
class Animal {
private name: string;
protected age: number;
public species: string;
constructor(name: string, age: number, species: string) {
this.name = name;
this.age = age;
this.species = species;
}
public makeSound(): void {
console.log(`${this.name} makes a sound`);
}
protected getAge(): number {
return this.age;
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age, "Dog");
this.breed = breed;
}
public bark(): void {
console.log(`${this.name} barks!`);
}
}
abstract class Shape {
abstract calculateArea(): number;
displayArea(): void {
console.log(`Area: ${this.calculateArea()}`);
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
calculateArea(): number {
return Math.PI * this.radius * this.radius;
}
}
访问修饰符
public: 公开访问(默认)
private: 私有访问,只能在类内部访问
protected: 受保护访问,类及其子类可访问
readonly: 只读属性
静态成员
class MathHelper {
static PI: number = 3.14159;
static calculateCircleArea(radius: number): number {
return this.PI * radius * radius;
}
}
console.log(MathHelper.PI);
console.log(MathHelper.calculateCircleArea(5));
7. 模块与命名空间
模块
export interface User {
id: number;
name: string;
}
export function getUser(id: number): User {
return { id, name: "John" };
}
export default class UserService {
}
import { User, getUser } from "./user";
import UserService from "./user";
import * as UserModule from "./user";
命名空间
namespace App {
export interface Config {
apiUrl: string;
timeout: number;
}
export function init(config: Config): void {
console.log("Initializing app...");
}
}
App.init({ apiUrl: "http://api.example.com", timeout: 5000 });
8. 装饰器
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args);
return originalMethod.apply(this, args);
};
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value: string;
const getter = () => value;
const setter = (newValue: string) => {
value = formatString.replace("%s", newValue);
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
};
}
class Person {
@format("Hello, %s!")
name: string;
}
9. 类型工具
内置工具类型
type PartialUser = Partial<User>;
type RequiredUser = Required<Partial<User>>;
type ReadonlyUser = Readonly<User>;
type UserMap = Record<string, User>;
type UserName = Pick<User, "name">;
type UserWithoutId = Omit<User, "id">;
type Numbers = Exclude<string | number, string>;
type Strings = Extract<string | number, string>;
type NonNullString = NonNullable<string | null>;
type AddReturn = ReturnType<typeof add>;
type AddParams = Parameters<typeof add>;
type PersonInstance = InstanceType<typeof Person>;
10. 类型断言与类型守卫
类型断言
let value: unknown = "hello";
let str: string = value as string;
let str2: string = <string>value;
let value2: unknown = "hello";
let str3: string = value2 as unknown as string;
类型守卫
function isString(value: unknown): value is string {
return typeof value === "string";
}
function isDate(value: unknown): value is Date {
return value instanceof Date;
}
function hasName(obj: unknown): obj is { name: string } {
return typeof obj === "object" && obj !== null && "name" in obj;
}
interface Fish {
swim: () => void;
}
interface Bird {
fly: () => void;
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
11. 类型推断
上下文类型
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((num) => {
console.log(num.toFixed(2));
});
const user = {
name: "John",
age: 25,
email: "john@example.com",
};
最佳通用类型
let arr = [0, 1, null];
let x = [0, 1, "hello"];
TypeScript 最佳实践
1. 类型定义
- 优先使用
interface 定义对象类型
- 使用
type 定义联合类型、交叉类型和工具类型
- 避免使用
any,优先使用 unknown
- 使用
readonly 标记不可变属性
2. 泛型使用
- 使用有意义的泛型参数名(T, U, V)
- 为泛型添加约束以提高类型安全性
- 避免过度使用泛型,保持代码简洁
3. 类型守卫
- 优先使用类型守卫而不是类型断言
- 使用
is 关键字创建自定义类型守卫
- 使用可辨识联合(Discriminated Unions)
4. 配置选项
- 启用
strict 模式以获得最佳类型安全
- 使用
noImplicitAny 避免隐式 any
- 使用
strictNullChecks 进行严格的空检查
5. 代码组织
- 使用模块而不是命名空间
- 合理组织文件结构
- 使用
index.ts 作为模块入口
- 为公共 API 导出类型声明
6. 性能优化
- 使用增量编译
- 合理使用
exclude 和 include
- 使用项目引用进行大型项目管理
- 避免过度复杂的类型定义
TypeScript 常见错误及解决方案
1. 类型不匹配错误
- 使用类型断言或类型守卫
- 检查类型定义是否正确
- 使用联合类型或交叉类型
2. 属性不存在错误
- 使用可选链操作符
?.
- 检查接口定义
- 使用索引签名
3. 泛型推断错误
- 显式指定泛型类型参数
- 添加泛型约束
- 使用类型默认值
4. 模块解析错误
- 检查
tsconfig.json 配置
- 使用相对路径导入
- 配置
baseUrl 和 paths
5. 类型声明文件错误
- 安装正确的
@types 包
- 手动编写类型声明
- 使用
declare module 扩展模块
TypeScript 配置详解
1. tsconfig.json 基础配置
基本结构
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"checkJs": false,
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"],
"extends": "@tsconfig/recommended/tsconfig.json",
"references": [{ "path": "./packages/core" }, { "path": "./packages/utils" }]
}
2. 编译选项详解
编译目标选项
target
{
"compilerOptions": {
"target": "ES2020"
}
}
- 指定编译目标 ECMAScript 版本
- 可选值:
ES3, ES5, ES6/ES2015, ES2016, ES2017, ES2018, ES2019, ES2020, ES2021, ES2022, ESNext
- 默认值:
ES3(如果 module 为 commonjs),否则为 ES5
- 影响:决定哪些 JavaScript 特性会被降级处理
lib
{
"compilerOptions": {
"lib": ["ES2020", "DOM", "DOM.Iterable", "WebWorker"]
}
}
- 指定包含的类型定义库
- 可选值:
ES5, ES6, ES7, ES2015, ES2016, ES2017, ES2018, ES2019, ES2020, ES2021, ES2022, ESNext, DOM, DOM.Iterable, WebWorker, ScriptHost, etc.
- 默认值:根据
target 自动选择
- 用途:控制可用的全局变量和类型定义
模块选项
module
{
"compilerOptions": {
"module": "ESNext"
}
}
- 指定模块系统
- 可选值:
None, CommonJS, AMD, System, UMD, ES6, ES2015, ES2020, ESNext, Node16, NodeNext
- 默认值:根据
target 自动选择
- 影响:决定模块导入导出的编译方式
moduleResolution
{
"compilerOptions": {
"moduleResolution": "node"
}
}
- 指定模块解析策略
- 可选值:
Node, Node16, NodeNext, Classic
- 默认值:
Classic(如果 module 为 AMD, System, ES6),否则为 Node
- Node16/NodeNext:支持 package.json 的
exports 字段和条件导出
baseUrl
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}
- 设置模块解析的基础目录
- 配合
paths 实现路径别名
- 默认值:包含 tsconfig.json 的目录
paths
{
"compilerOptions": {
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@types/*": ["src/types/*"],
"lodash-es": ["node_modules/lodash-es"]
}
}
}
- 配置模块路径映射
- 支持通配符
*
- 必须配合
baseUrl 使用
rootDir
{
"compilerOptions": {
"rootDir": "./src"
}
}
- 指定源代码的根目录
- 控制输出目录结构
- 默认值:自动推断
outDir
{
"compilerOptions": {
"outDir": "./dist"
}
}
outFile
{
"compilerOptions": {
"module": "AMD",
"outFile": "./dist/bundle.js"
}
}
- 将所有输出合并到一个文件
- 仅适用于
module: AMD 或 System
类型检查选项
strict
{
"compilerOptions": {
"strict": true
}
}
- 启用所有严格类型检查选项
- 包含:
strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitAny, noImplicitThis, alwaysStrict
- 推荐在新项目中启用
strictNullChecks
{
"compilerOptions": {
"strictNullChecks": true
}
}
- 启用严格的 null 检查
null 和 undefined 只能赋值给 any、null、undefined 类型
- 推荐启用
noImplicitAny
{
"compilerOptions": {
"noImplicitAny": true
}
}
- 禁止隐式
any 类型
- 函数参数必须有类型注解
- 推荐启用
strictFunctionTypes
{
"compilerOptions": {
"strictFunctionTypes": true
}
}
strictPropertyInitialization
{
"compilerOptions": {
"strictPropertyInitialization": true
}
}
- 检查类属性是否被初始化
- 非空属性必须在构造函数中初始化或使用
! 断言
noImplicitThis
{
"compilerOptions": {
"noImplicitThis": true
}
}
- 禁止
this 隐式具有 any 类型
- 要求明确指定
this 的类型
alwaysStrict
{
"compilerOptions": {
"alwaysStrict": true
}
}
- 在编译输出中包含
"use strict"
- 适用于 ES5 及以下
额外检查选项
noUnusedLocals
{
"compilerOptions": {
"noUnusedLocals": true
}
}
noUnusedParameters
{
"compilerOptions": {
"noUnusedParameters": true
}
}
- 检查未使用的函数参数
- 可以使用
_ 前缀标记为有意未使用
noImplicitReturns
{
"compilerOptions": {
"noImplicitReturns": true
}
}
noFallthroughCasesInSwitch
{
"compilerOptions": {
"noFallthroughCasesInSwitch": true
}
}
- 检查 switch 语句的穿透情况
- 要求每个 case 都有 break 或 return
noUncheckedIndexedAccess
{
"compilerOptions": {
"noUncheckedIndexedAccess": true
}
}
- 检查数组/对象索引访问的安全性
- 索引访问返回
T | undefined
互操作性选项
esModuleInterop
{
"compilerOptions": {
"esModuleInterop": true
}
}
- 启用 ES 模块互操作性
- 允许使用
import 导入 CommonJS 模块
- 推荐启用
allowSyntheticDefaultImports
{
"compilerOptions": {
"allowSyntheticDefaultImports": true
}
}
- 允许从没有默认导出的模块进行默认导入
- 通常与
esModuleInterop 一起使用
allowJs
{
"compilerOptions": {
"allowJs": true
}
}
- 允许导入 JavaScript 文件
- 适用于渐进式迁移到 TypeScript
checkJs
{
"compilerOptions": {
"checkJs": true
}
}
- 检查 JavaScript 文件的类型错误
- 需要
allowJs: true
jsx
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
- 指定 JSX 的编译方式
- 可选值:
preserve, react, react-native, react-jsx, react-jsxdev
输出选项
declaration
{
"compilerOptions": {
"declaration": true
}
}
declarationMap
{
"compilerOptions": {
"declarationMap": true
}
}
- 生成
.d.ts.map 声明文件 source map
- 支持编辑器跳转到源代码
sourceMap
{
"compilerOptions": {
"sourceMap": true
}
}
- 生成
.js.map source map 文件
- 支持调试
removeComments
{
"compilerOptions": {
"removeComments": true
}
}
emitDeclarationOnly
{
"compilerOptions": {
"emitDeclarationOnly": true
}
}
- 只生成类型声明文件,不生成 JavaScript
- 适用于使用 Babel 或 SWC 编译的情况
其他选项
incremental
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo"
}
}
skipLibCheck
{
"compilerOptions": {
"skipLibCheck": true
}
}
- 跳过类型声明文件的类型检查
- 提高编译速度
- 推荐启用
resolveJsonModule
{
"compilerOptions": {
"resolveJsonModule": true
}
}
types
{
"compilerOptions": {
"types": ["node", "jest", "webpack-env"]
}
}
- 指定要包含的类型声明包
- 空数组表示禁用自动类型包含
typeRoots
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./src/types"]
}
}
- 指定类型声明文件的查找目录
- 默认:
./node_modules/@types
3. 项目配置
include
{
"include": ["src/**/*", "tests/**/*"]
}
- 指定要包含的文件模式
- 支持 glob 模式
- 默认:包含所有
.ts, .tsx, .d.ts 文件
exclude
{
"exclude": ["node_modules", "dist", "build", "**/*.spec.ts", "**/*.test.ts"]
}
- 指定要排除的文件模式
- 默认排除:
node_modules, bower_components, jspm_packages
files
{
"files": ["src/index.ts", "src/utils.ts"]
}
- 显式指定要编译的文件列表
- 优先级高于
include 和 exclude
extends
{
"extends": "@tsconfig/recommended/tsconfig.json"
}
- 继承基础配置
- 可以继承多个配置(数组)
- 支持相对路径和 npm 包
references
{
"references": [{ "path": "./packages/core" }, { "path": "./packages/utils" }]
}
- 项目引用
- 支持多项目构建
- 需要配合
composite: true 使用
TypeScript 编译器命令
基本命令
tsc
tsc --watch
tsc --init
tsc --version
tsc --help
tsc --project tsconfig.build.json
tsc --noEmit
tsc --verbose
tsc --force
常用编译选项
tsc --outDir dist
tsc src/index.ts
tsc --declaration
tsc --sourceMap
tsc --removeComments
tsc file1.ts file2.ts
tsc --pretty false
6. TypeScript 配置最佳实践
推荐的基础配置
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"incremental": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
性能优化配置
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo",
"skipLibCheck": true,
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
}
}
严格模式配置
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true
}
}