
T1:TypeScript 中命名空间与模块的理解?区别?
模块(Module):
- ES6 标准,基于文件的模块系统(
import/export); - 可以是
export任何顶级声明(变量、函数、类、接口); - 由模块加载器(ESM、CommonJS、AMD)负责依赖管理。
命名空间(Namespace):
- TypeScript 早期为解决全局变量污染引入的内部模块机制;
- 使用
namespace X { ... }声明,内部export导出成员; - 最终被编译为 IIFE,依赖全局对象;
- 不推荐在新项目使用,仅适用于声明文件(
.d.ts)和全局类型扩展。
// 命名空间(不推荐)
namespace Utils {
export function format(date: Date) { /* ... */ }
}
// 模块(推荐)
// utils.ts
export function format(date: Date) { /* ... */ }区别对比:
| 维度 | 模块 | 命名空间 |
|---|---|---|
| 范围 | 文件级 | 块级 |
| 加载 | 模块系统 | 全局对象 |
| 推荐场景 | 所有现代项目 | 仅 .d.ts 中使用 |
T2:TypeScript 是什么?与 JavaScript 的区别?
TypeScript 是 JavaScript 的超集,添加了静态类型系统和现代语言特性,最终编译为纯 JavaScript。
核心区别:
| 维度 | JavaScript | TypeScript |
|---|---|---|
| 类型系统 | 动态类型,运行时检查 | 静态类型,编译时检查 |
| 错误检测 | 运行时崩溃 | 编译时报警 |
| 开发体验 | 需手动测试 | 智能提示 + 重构 |
| 适用场景 | 小型脚本、快速迭代 | 中大型项目、团队协作 |
| 学习曲线 | 低 | 中(需理解类型) |
| 运行时性能 | 无额外开销 | 编译后无运行时类型检查 |
关键优势:
- 静态类型提前发现问题;
- 强大的 IDE 支持(VS Code 智能补全、跳转、重构);
- 现代特性提前使用(装饰器、可选链、空值合并等,编译到目标 ES 版本);
- 更适合大型项目维护。
T3:TypeScript 中泛型是什么?
泛型(Generics):在定义函数、接口、类时不预先指定类型,使用时再指定类型的特性。
// 泛型函数
function identity<T>(value: T): T {
return value;
}
identity<string>('hello'); // T = string
identity(42); // T 自动推断为 number
// 泛型接口
interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
const res: ApiResponse<User> = { code: 0, data: { name: 'Tom' }, message: 'ok' };
// 泛型类
class Stack<T> {
private items: T[] = [];
push(item: T) { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
}
// 泛型约束
interface Lengthwise { length: number; }
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}作用:编写可复用、类型安全的代码,避免使用 any 丢失类型。
T4:TypeScript 中有哪些声明变量的方式?
// 1. var(不推荐,函数作用域)
var a = 1;
// 2. let(块作用域,可重新赋值)
let b = 2;
// 3. const(块作用域,不可重新赋值)
const c = 3;
// 4. 解构声明
const { x, y } = { x: 1, y: 2 };
const [first, ...rest] = [1, 2, 3];
// 5. 类型断言声明
const el = document.querySelector('#app') as HTMLDivElement;
const len = (<string>someValue).length;
// 6. 函数声明
function foo() {}
const bar = function() {};
// 7. 接口/类型别名
interface User { name: string; }
type Pair = [number, number];
// 8. 类声明
class Animal {}
// 9. 枚举声明
enum Color { Red, Green, Blue }
// 10. namespace / module 声明
namespace Utils { export const PI = 3.14; }T5:TypeScript 的方法重载是什么?
概念:同一个函数名定义多个类型签名,编译器按调用实参匹配最合适的一个。
// 签名列表(无函数体)
function add(a: number, b: number): number;
function add(a: string, a: string): string;
// 实现签名(必须兼容所有重载)
function add(a: any, b: any): any {
return a + b;
}
add(1, 2); // 3
add('a', 'b'); // 'ab'
// add(true, false); // 报错注意:
- 重载是编译时的概念,运行时仍是单个函数;
- 重载列表必须由上到下从最具体到最宽泛排列;
- 实现签名对外不可见。
T6:实现 sleep 方法
// Promise 版(推荐)
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 使用
async function run() {
console.log('start');
await sleep(1000);
console.log('after 1s');
}T7:TypeScript 中 is 关键字有什么用?
类型谓词(Type Predicate):用于在自定义类型守卫函数中收窄类型。
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // 此处 pet 被收窄为 Fish
} else {
pet.fly(); // 此处 pet 被收窄为 Bird
}
}作用:告诉编译器某个条件返回 true 时参数的具体类型,提升类型推断准确性。
T8:TypeScript 支持的访问修饰符有哪些?
class User {
public name: string; // 默认,任何位置可访问
private age: number; // 仅本类内部
protected role: string; // 本类及子类
readonly id: number; // 只读,构造后不可改
constructor(name: string, age: number, role: string, id: number) {
this.name = name;
this.age = age;
this.role = role;
this.id = id;
}
}
// 参数属性简化写法
class User2 {
constructor(
public name: string,
private age: number,
protected role: string,
readonly id: number
) {}
}
// private 在编译期生效,运行时仍是公共字段
// 如需运行时保护,使用 # 私有字段(ES2022)
class A {
#secret = 123; // 真正的私有
}T9:实现 myMap 方法
// 类似 Array.prototype.map
function myMap<T, U>(arr: T[], callback: (item: T, index: number, array: T[]) => U): U[] {
const result: U[] = [];
for (let i = 0; i < arr.length; i++) {
result.push(callback(arr[i], i, arr));
}
return result;
}T10:实现 treePath 方法
题目:给定树形结构(带 id / children),返回从根到目标节点的路径。
interface TreeNode { id: number; children?: TreeNode[]; }
function treePath(root: TreeNode, target: number, path: number[] = []): number[] | null {
path.push(root.id);
if (root.id === target) return path;
for (const child of root.children || []) {
const found = treePath(child, target, path);
if (found) return found;
}
path.pop();
return null;
}T11:实现 product 方法(笛卡尔积 / 多数组组合)
function product<T>(...arrays: T[][]): T[][] {
if (arrays.length === 0) return [[]];
return arrays.reduce((acc, arr) =>
acc.flatMap(a => arr.map(b => [...a, b])), [[]] as T[][]
);
}
product([1, 2], ['a', 'b'], [true, false]);
// [
// [1, 'a', true], [1, 'a', false],
// [1, 'b', true], [1, 'b', false],
// [2, 'a', true], [2, 'a', false],
// [2, 'b', true], [2, 'b', false]
// ]T12:实现 myAll 方法(Promise.all 类型安全版)
function myAll<T>(promises: Promise<T>[]): Promise<T[]> {
return new Promise((resolve, reject) => {
const result: T[] = [];
let count = 0;
if (promises.length === 0) return resolve([]);
promises.forEach((p, i) => {
Promise.resolve(p).then(
val => { result[i] = val; if (++count === promises.length) resolve(result); },
reject
);
});
});
}T13:实现 sum 方法(柯里化求和)
function sum(...args: number[]): number {
return args.reduce((a, b) => a + b, 0);
}
// 柯里化版 sum(1)(2)(3)(4)() = 10
function curriedSum(...args: number[]): any {
const inner = (...rest: number[]) => curriedSum(...args, ...rest);
inner.valueOf = () => args.reduce((a, b) => a + b, 0);
return inner;
}T14:实现 mergeArray 方法(数组合并去重)
function mergeArray<T>(...arrays: T[][]): T[] {
return [...new Set(arrays.flat())];
}
// 复杂对象去重(指定 key)
function mergeByKey<T>(arrays: T[][], key: keyof T): T[] {
const map = new Map<T[keyof T], T>();
arrays.flat().forEach(item => map.set(item[key], item));
return [...map.values()];
}T15:实现 firstSingleChar 方法
题目:找到字符串中第一个只出现一次的字符。
function firstSingleChar(str: string): string | null {
const counts = new Map<string, number>();
for (const ch of str) counts.set(ch, (counts.get(ch) || 0) + 1);
for (const ch of str) {
if (counts.get(ch) === 1) return ch;
}
return null;
}T16:实现 reverseWord 方法(按单词反转字符串)
function reverseWord(str: string): string {
return str.trim().split(/\s+/).reverse().join(' ');
}T17:定义混合类型数组(元素可能是 string 或 number)
// 联合类型
const arr1: (string | number)[] = [1, 'a', 2, 'b'];
// 元组(位置固定)
const arr2: [string, number, string] = ['a', 1, 'b'];
// 更复杂的场景:每项是不同结构
type Item = { type: 'text'; value: string } | { type: 'num'; value: number };
const arr3: Item[] = [
{ type: 'text', value: 'hello' },
{ type: 'num', value: 42 },
];
// 使用时类型守卫收窄
arr3.forEach(item => {
if (item.type === 'text') console.log(item.value.toUpperCase());
else console.log(item.value.toFixed(2));
});T18:补充 objToArray 函数
题目:{ a: 1, b: 2 } → [['a', 1], ['b', 2]]
function objToArray<T>(obj: Record<string, T>): [string, T][] {
return Object.entries(obj);
}
// 或自定义转换
function objToArray2<T>(obj: Record<string, T>, keyName = 'key', valueName = 'value') {
return Object.entries(obj).map(([k, v]) => ({ [keyName]: k, [valueName]: v }));
}T19:使用 TS 判断参数是否是数组
// 方法1:Array.isArray(推荐)
function isArray<T>(arg: unknown): arg is T[] {
return Array.isArray(arg);
}
// 方法2:instanceof
function isArray2<T>(arg: unknown): arg is T[] {
return arg instanceof Array;
}T20:TypeScript 的内置数据类型有哪些?
// 原始类型
let s: string = 'hello';
let n: number = 42;
let b: boolean = true;
let nu: null = null;
let un: undefined = undefined;
let sy: symbol = Symbol('id');
let bi: bigint = 10n;
// 对象类型
let obj: object = {};
let fn: Function = () => {};
// 特殊类型
let anyT: any = 'anything'; // 绕过类型检查
let unk: unknown = 'safe'; // 安全的 any,使用前需收窄
let nev: never = (() => { throw new Error(); })(); // 不可能存在的值
let voi: void = undefined; // 无返回值
// 复合类型
let uni: string | number = 'a';
let inter: { name: string } & { age: number } = { name: 'Tom', age: 18 };
// 字面量类型
let lit: 'small' | 'medium' | 'large' = 'medium';
// 集合类型
let arr: number[] = [1, 2];
let tup: [string, number] = ['a', 1];
// 包装类型
let strObj: String = new String('a'); // 不推荐T21:any 和 unknown 有什么区别?
| 维度 | any | unknown |
|---|---|---|
| 类型检查 | 绕过所有检查 | 使用前必须收窄 |
| 可赋值给 | 任何类型 | 仅 any / unknown |
| 可调用 | 直接调用 | 必须先收窄才能调用 |
| 安全性 | 不安全 | 类型安全 |
最佳实践:默认用 unknown,必要时用类型守卫收窄。
let a: any = 'hello';
a.toFixed(); // 编译通过,运行可能报错
let u: unknown = 'hello';
// u.toFixed(); // 编译报错
if (typeof u === 'string') {
u.toUpperCase(); // 收窄为 string 后安全使用
}T22:如何将 unknown 类型指定为更具体的类型?
三种方式:
类型断言(强制,不做检查):
const v: unknown = 'hello'; const s = v as string;类型守卫(推荐):
if (typeof v === 'string') { /* v 为 string */ } if (v instanceof Date) { /* v 为 Date */ } if (Array.isArray(v)) { /* v 为数组 */ }自定义类型谓词:
interface User { name: string; } function isUser(x: unknown): x is User { return typeof x === 'object' && x !== null && 'name' in x; }
T23:使用 TS 实现入参是否是数组的判断
// 类型谓词版
function isArray<T>(arg: unknown): arg is T[] {
return Array.isArray(arg);
}
// 更严格的判断(只接受非空数组)
function isNonEmptyArray<T>(arg: unknown): arg is [T, ...T[]] {
return Array.isArray(arg) && arg.length > 0;
}T24:tsconfig.json 文件有什么用?
作用:TypeScript 编译器的配置文件,控制编译行为。
常用配置:
{
"compilerOptions": {
"target": "ES2020", // 编译目标 JS 版本
"module": "ESNext", // 模块系统
"moduleResolution": "bundler", // 模块解析策略
"lib": ["DOM", "ES2020"], // 类型库
"jsx": "react-jsx", // JSX 处理方式
"strict": true, // 严格模式总开关
"noImplicitAny": true, // 禁止隐式 any
"strictNullChecks": true, // 严格 null 检查
"strictFunctionTypes": true,
"esModuleInterop": true, // 允许 default import 语法
"allowSyntheticDefaultImports": true,
"skipLibCheck": true, // 跳过 .d.ts 类型检查
"forceConsistentCasingInFileNames": true,
"baseUrl": "./",
"paths": { // 路径别名
"@/*": ["src/*"]
},
"outDir": "./dist",
"rootDir": "./src",
"sourceMap": true,
"declaration": true // 生成 .d.ts
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}T25:TypeScript 中 Declare 关键字有什么用?
作用:告诉编译器"该变量/模块已存在",避免类型错误。
// 1. 声明全局变量
declare const VERSION: string;
declare const __DEV__: boolean;
// 2. 声明全局函数
declare function greet(name: string): void;
// 3. 声明全局类
declare class MyLib { constructor(opts?: any); }
// 4. 声明模块(最常见:补充没有类型的 npm 包)
declare module 'my-lib' {
export function init(config: object): void;
export const version: string;
}
// 5. 声明命名空间扩展
declare global {
interface Window {
myApp: { foo(): void };
}
}
// 6. 声明模块扩展(UMD)
declare module '*.css' {
const content: string;
export default content;
}T26:解释 TypeScript 中的枚举
枚举(Enum):为一组数值赋予有意义的名字。
// 数字枚举(自动递增)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 字符串枚举(推荐)
enum Status {
Pending = 'PENDING',
Success = 'SUCCESS',
Error = 'ERROR'
}
// 常量枚举(编译时内联)
const enum Color {
Red = '#FF0000',
Green = '#00FF00'
}
// 反向映射(仅数字枚举)
enum Role { Admin, User }
const name: string = Role[0]; // 'Admin'
// 使用
function setStatus(s: Status) { /* ... */ }
setStatus(Status.Success);注意:
- 普通枚举会产生额外 JS 代码;
- 字符串枚举不会反向映射;
推荐使用 字面量联合类型 代替枚举(更轻量、Tree-shakable):
type Status = 'PENDING' | 'SUCCESS' | 'ERROR';
T27:TypeScript 的主要特点是什么?
- 静态类型系统:编译期类型检查,提前发现错误。
- 类型推断:自动推导变量类型,减少冗余标注。
- 现代 JS 特性:支持 ES6+、装饰器、可选链、空值合并等,编译到旧版本。
- 强大的泛型:编写可复用、类型安全的代码。
- 结构化类型(鸭子类型):只要形状兼容即兼容,灵活。
- 渐进式采用:可以与 JS 共存,逐步迁移。
- 丰富工具链:VS Code 一流支持,智能提示、重构、跳转。
- 高级类型:联合类型、交叉类型、条件类型、映射类型、模板字面量类型。
- 装饰器:支持类与方法元编程(Stage 3)。
- 生态丰富:几乎所有主流库都有 .d.ts 类型定义。