跳到主要内容 TypeScript 前端高频面试题 | 极客日志
Python
TypeScript 前端高频面试题 TypeScript 前端高频面试题 请解释 TypeScript 是什么?它与 JavaScript 的核心区别是什么? 面试回答需突出 TS 的核心价值(类型安全)和与 JS 的关键差异,结构清晰: **TypeScript 定义**:TS 是 JavaScript 的超集(Superset),在 JS 语法基础上增加了**静态类型系统**,最终会编译为纯 JS 运行(支持所有 JS 环境),…
古灵精怪 发布于 2026/4/6 更新于 2026/4/13 29K 浏览TypeScript 前端高频面试题
1. 请解释 TypeScript 是什么?它与 JavaScript 的核心区别是什么?
面试回答需突出 TS 的核心价值(类型安全)和与 JS 的关键差异,结构清晰:
TypeScript 定义 :TS 是 JavaScript 的超集(Superset),在 JS 语法基础上增加了静态类型系统 ,最终会编译为纯 JS 运行(支持所有 JS 环境),核心目标是提升代码可维护性、减少运行时错误。
与 JavaScript 的核心区别 (分点对比):
类型系统 :TS 有静态类型(编译阶段检查类型,变量声明时需指定/推断类型);JS 是动态类型(运行时才确定类型,易出现类型错误)。
编译阶段 :TS 需通过编译器(如 tsc)编译为 JS 才能运行(编译时会做类型校验);JS 可直接在浏览器/Node 环境运行。
特性补充 :TS 新增接口(Interface)、泛型(Generic)、枚举(Enum)、类型守卫等特性;JS 无这些原生类型相关特性。
开发体验 :TS 支持 IDE 智能提示、自动补全、类型错误提前预警;JS 开发时需手动判断类型,错误难提前发现。
2. TypeScript 支持哪些基本数据类型?请分别说明。 需覆盖原始类型、特殊类型,明确各类型的用途,实习中常用类型优先:
原始类型 (与 JS 一致,需显式标注):
string:字符串类型,如 let name: string = "张三"。
number:数字类型(含整数、浮点数),如 let age: number = 22。
boolean:布尔类型(true/false),如 let isStudent: boolean = true。
null:空值类型,需开启 strictNullChecks 才会单独识别,如 let empty: null = null。
undefined:未定义类型,同上,如 let unassigned: undefined = undefined。
symbol:唯一标识类型(ES6 特性),如 let id: symbol = Symbol("uniqueId")。
bigint:大整数类型(处理超出 number 范围的数值),如 let bigNum: bigint = 100n。
特殊类型 (TS 新增,实习高频):
any:任意类型(关闭类型检查,不推荐滥用),如 let random: any = "hello"(后续可赋值为数字)。
unknown:未知类型(比 any 安全,需类型断言后使用),如 let value: unknown = 123(需 value as number 后才能调用数字方法)。
void:无返回值类型(常用于函数返回值),如 function log(): void { console.log("log") }。
never:永不存在的类型(如抛出错误的函数、无限循环函数的返回值),如 function throwErr(): never { throw new Error("err") }。
3. 接口(Interface)和类型别名(Type Alias)的区别是什么?分别在什么场景下使用? 这是实习面试必问(易混淆),需分'相同点''不同点''使用场景':
相同点 :
均可定义对象/函数类型,如 interface User { name: string } 和 type User = { name: string }。
均可支持泛型,如 interface Box<T> { value: T } 和 type Box<T> = { value: T }。
核心区别 :
扩展方式 :
Interface:通过 extends 扩展,如 interface Student extends User { age: number }。
Type Alias:通过交叉类型(&)扩展,如 type Student = User & { age: number }。
合并能力 :
Interface:支持'声明合并'(同名接口会自动合并属性),如 interface User { name: string } 和 interface User { age: number } 合并为 { name: string; age: number }。
Type Alias:不支持声明合并(同名会报错)。
适用范围 :
Interface:仅能定义对象/函数类型,不能定义基础类型(如 interface Num = number 报错)。
Type Alias:可定义任意类型(基础类型、联合类型、交叉类型等),如 type StrOrNum = string | number。
使用场景 :
优先用 Interface:定义组件 Props、API 返回数据结构等需'可扩展/合并'的对象类型(符合团队协作中类型迭代的需求)。
优先用 Type Alias:定义联合类型、交叉类型、基础类型别名(如 type ID = string | number),或需要复用复杂类型时。
4. 什么是泛型(Generic)?为什么需要泛型?请举一个实习中常用的例子。 泛型是 TS 核心特性(实习中复用组件/工具函数必用),需讲清'定义''作用''实例':
泛型定义 :泛型是'类型参数化'的语法,允许在定义函数、接口、类时不指定具体类型,而是在使用时动态传入类型(类似函数的参数传递),语法用 <T>(T 为类型占位符,可自定义名称)。
为什么需要泛型 (解决两个核心问题):
类型安全 :避免使用 any 导致的类型丢失(如用 any 定义数组,无法约束数组元素类型)。
代码复用 :一套逻辑支持多种类型(如一个'获取数组第一个元素'的函数,可同时支持 string 数组、number 数组)。
function getFirstElement<T>(arr : T[]): T | undefined {
return arr[0 ];
}
const strArr : string [] = ["a" , "b" ];
const firstStr = getFirstElement (strArr);
const numArr : number [] = [1 , 2 ];
const firstNum = getFirstElement<number >(numArr);
5. 什么是类型断言(Type Assertion)?有哪几种方式?使用时需要注意什么? 类型断言是'手动指定类型'的语法(实习中操作 DOM、处理未知类型时常用),需讲清'定义''方式''注意事项':
类型断言定义 :当 TS 无法自动推断变量类型时,开发者明确告知 TS'该变量的实际类型',强制 TS 按指定类型处理(仅编译阶段生效,不影响运行时)。
两种常用方式 :
不能断言'完全无关的类型'(如 let num: number = 123 as string 会报错),仅能断言'兼容的类型'(如 unknown 断言为 string,HTMLElement 断言为 HTMLInputElement)。
避免滥用:优先让 TS 自动推断类型,仅在类型不确定时使用(滥用会抵消 TS 的类型安全优势)。
尖括号语法 (不支持 JSX 场景,易与 JSX 标签冲突):
const input = <HTMLInputElement >document .getElementById ("username" );
const input = document .getElementById ("username" ) as HTMLInputElement ;
input.value = "test" ;
6. any、unknown、never、void 这四种特殊类型的区别是什么?分别在什么场景下使用? 这四种类型易混淆,需对比说明'含义''使用场景',突出实习中的高频用法:
类型 核心含义 关键特性 实习常用场景 any任意类型(关闭类型检查) 可赋值给任意类型,任意类型可赋值给它;可调用任意方法(无类型校验) 临时兼容旧 JS 代码(不推荐主动使用) unknown未知类型(安全的 any) 仅能赋值给 any/unknown;需断言后才能调用方法 接收未知来源的值(如 API 返回数据、用户输入) void无返回值 仅函数返回值可用;仅 undefined 可赋值给它(开启 strictNullChecks 时) 定义无返回值的函数(如 console.log 类函数) never永不存在的类型 不能赋值给任何类型,任何类型也不能赋值给它;无任何属性/方法 定义抛出错误的函数、无限循环函数的返回值
let anyVal : any = "hello" ;
anyVal ();
let unknownVal : unknown = "hello" ;
(unknownVal as string ).length ;
function logMsg ( ): void {
console .log ("msg" );
}
function infiniteLoop ( ): never {
while (true ) {}
}
7. 如何在 TypeScript 中定义'可选属性'和'只读属性'?请举例说明。 这是定义对象类型的基础(实习中定义组件 Props、API 结构常用),需讲清'语法''特性''例子':
1. 可选属性 :
语法:在属性名后加 ?,表示该属性'可存在、可不存在'。
特性:访问可选属性时,TS 会自动判断是否为 undefined(避免运行时错误)。
2. 只读属性 :
语法:在属性名前加 readonly,表示该属性'初始化后不能修改'。
特性:仅限制'重新赋值',若属性是对象,对象内部属性仍可修改(浅只读)。
interface User {
readonly id : string ;
name : string ;
}
const user : User = {
id : "123" ,
name : "张三"
};
user.name = "李四" ;
user.id = "456" ;
interface User {
name : string ;
age ?: number ;
}
const user1 : User = {
name : "张三"
};
const user2 : User = {
name : "李四" ,
age : 22
};
8. 什么是联合类型(Union Type)和交叉类型(Intersection Type)?请分别举例说明使用场景。 两种类型组合方式(实习中处理'多类型参数''合并类型'常用),需对比'定义''语法''例子':
1. 联合类型(Union Type) :
定义:表示变量的类型是'多个类型中的任意一个',用 | 分隔类型,核心是'或'的关系。
语法:TypeA | TypeB | TypeC。
实习场景:处理'参数可能有多种类型'的情况(如函数参数支持 string 或 number)。
2. 交叉类型(Intersection Type) :
定义:表示变量的类型是'多个类型的合并',包含所有类型的属性/方法,用 & 分隔类型,核心是'且'的关系。
语法:TypeA & TypeB & TypeC。
实习场景:处理'合并多个类型'的情况(如合并基础用户信息和权限信息)。
例子(合并 User 和 Permission 类型):
interface User {
name : string ;
age : number ;
}
interface Permission {
role : string ;
hasEdit : boolean ;
}
type UserWithPermission = User & Permission ;
const admin : UserWithPermission = {
name : "张三" ,
age : 22 ,
role : "admin" ,
hasEdit : true ,
};
type ID = string | number ;
function getUserById (id : ID ): void {
if (typeof id === "string" ) {
console .log ("字符串 ID:" , id);
} else {
console .log ("数字 ID:" , id);
}
}
getUserById ("123" );
getUserById (456 );
9. 什么是类型守卫(Type Guard)?常用的类型守卫有哪些?请举例说明。 类型守卫是'判断联合类型具体类型'的工具(实习中处理联合类型逻辑必用),需讲清'定义''常用方式''例子':
类型守卫定义 :在运行时检查变量的类型,让 TS 在代码块内部自动推断出变量的具体类型(缩小类型范围,避免类型错误)。
常用类型守卫方式 (按实习高频度排序):
自定义类型守卫 (通过函数返回 param is Type 语法自定义):
interface User {
id : string ;
name : string ;
}
function isUser (data : unknown ): data is User {
return (
typeof data === "object" &&
data !== null &&
"id" in data &&
"name" in data &&
typeof (data as User ).id === "string"
);
}
const apiData : unknown = {
id : "123" ,
name : "张三"
};
if (isUser (apiData)) {
console .log (apiData.name );
}
interface Dog {
bark : () => void ;
}
interface Cat {
meow : () => void ;
}
function makeSound (animal : Dog | Cat ): void {
if ("bark" in animal) {
animal.bark ();
} else {
animal.meow ();
}
}
instanceof 守卫 (判断引用类型:数组、类实例等):
type ArrOrObj = number [] | { value : number };
function getValue (data : ArrOrObj ): number {
if (data instanceof Array ) {
return data[0 ];
} else {
return data.value ;
}
}
typeof 守卫 (判断基础类型:string/number/boolean/symbol):
type StrOrNum = string | number ;
function getLength (value : StrOrNum ): number {
if (typeof value === "string" ) {
return value.length ;
} else {
return value.toString ().length ;
}
}
10. 如何将 TypeScript 集成到 Vue 项目中?(实习高频,以 Vue3 + Vite 为例) Vue 是前端实习高频框架,需讲清'集成步骤''核心配置''实习常用场景(如组件 Props 类型)':
1. 初始化 Vue + TS 项目 (Vite 方式,最常用):
npm create vite@latest my-vue-ts-project -- --template vue-ts
cd my-vue-ts-project
npm install
npm run dev
tsconfig.json:TS 编译配置,核心字段:
compilerOptions.strict: true:开启严格模式(强制类型校验,推荐开启)。
compilerOptions.lib: ["ESNext", "DOM"]:指定依赖的库(支持 ES 新特性和 DOM API)。
compilerOptions.types: ["vite/client"]:引入 Vite 客户端类型(支持 import.meta.env 等)。
vite.config.ts:Vite 配置文件(用 TS 编写,需导出 defineConfig 对象)。
3. 实习常用场景:组件中使用 TS (以 <script setup lang="ts"> 为例,Vue3 推荐语法):
<template>
<div>{{ username }} ({{ age }})</div>
<button @click="updateAge">增加年龄</button>
</template>
<script setup lang="ts">
// 1. 定义 Props 类型(用 defineProps + 泛型)
const props = defineProps<{
username: string; // 必选 Props
age?: number; // 可选 Props
}>();
// 2. 定义响应式变量(TS 自动推断类型)
import { ref } from "vue";
const count = ref(0); // TS 推断 count 为 Ref<number>
// 3. 定义函数(指定参数/返回值类型)
function updateAge(): void {
if (props.age) { // 若 age 存在,+1(TS 自动推断 props.age 为 number)
console.log(props.age + 1);
}
}
</script>
11. 什么是枚举(Enum)?它有什么作用?请举例说明数字枚举和字符串枚举的区别。 枚举是'命名常量集合'(实习中处理'固定状态'常用,如订单状态、按钮类型),需讲清'定义''分类''例子':
枚举定义 :TS 中用于定义'一组有名字的常量',使代码更具可读性(替代魔法值,如用 OrderStatus.PENDING 替代 1),语法用 enum 关键字。
核心作用 :
提高代码可读性(常量有语义化名称)。
约束取值范围(只能从枚举中选择值,避免非法值)。
两种常用枚举类型 :
字符串枚举 (值为字符串,需显式指定每个值,无自动递增):
enum ButtonType {
PRIMARY = "primary" ,
SECONDARY = "secondary" ,
DANGER = "danger" ,
}
function renderButton (type : ButtonType ): void {
console .log (`渲染 ${type } 类型按钮` );
}
renderButton (ButtonType .PRIMARY );
renderButton ("normal" );
enum OrderStatus {
PENDING ,
PAID ,
SHIPPED ,
DELIVERED ,
}
const status : OrderStatus = OrderStatus .PAID ;
console .log (status);
console .log (OrderStatus [1 ]);
实习使用建议 :优先用字符串枚举(语义更清晰,无反向映射冗余),仅在需要数字标识时用数字枚举。
12. 什么是声明文件(.d.ts)?它的作用是什么?请举例说明使用场景。 声明文件是'描述 JS 模块类型'的文件(实习中引入第三方 JS 库时常用),需讲清'定义''作用''例子':
声明文件定义 :以 .d.ts 为后缀的文件,用于告诉 TS'某个 JS 模块/变量的类型'(仅包含类型声明,无具体代码逻辑),让 TS 能识别 JS 代码的类型。
核心作用 :解决'TS 无法识别 JS 模块类型'的问题,常见场景:
引入无 TS 类型的第三方 JS 库(如一些老的 jQuery 插件)。
自定义 JS 模块,需为其提供类型声明(如实习中封装的 JS 工具函数)。
实习常用场景例子 :
场景 1:为第三方 JS 库写声明文件(如引入 lodash 无类型时):
declare module "lodash" {
export function debounce (
func : (...args: any []) => void ,
wait : number
): (...args : any [] ) => void ;
}
import { debounce } from "lodash" ;
const debouncedFn = debounce (() => console .log ("debounce" ), 1000 );
场景 2:为自定义 JS 工具函数写声明文件(如 utils.js):
export function formatDate (date, format ) {
return formattedDate;
}
declare module "./utils" {
export function formatDate (date : Date | string , format : string ): string ;
}
import { formatDate } from "./utils" ;
formatDate (new Date (), "YYYY-MM-DD" );
formatDate (123 , "YYYY-MM-DD" );
13. 如何在 TypeScript 中处理异步函数的类型(如 Promise)?请举例说明。 异步函数(API 请求)是实习必备,需讲清'Promise 泛型''async/await 类型推断''错误处理':
核心原则 :异步函数的返回值是 Promise<T> 类型,T 是异步操作成功后返回的数据类型(如 API 返回的用户数据类型)。
关键说明 :
Promise<T> 中的 T 必须与异步操作成功后的返回值类型一致(如 fetchUsers 成功返回 User[],故为 Promise<User[]>)。
async 函数的返回值默认是 Promise<void>(若无 return),若有 return 值,TS 会自动推断为 Promise<返回值类型>。
interface User {
id : string ;
name : string ;
age : number ;
}
function fetchUsers ( ): Promise <User []> {
return fetch ("https://api.example.com/users" )
.then ((response ) => {
if (!response.ok ) {
throw new Error ("请求失败" );
}
return response.json () as Promise <User []>;
})
.catch ((error ) => {
console .error ("获取用户失败:" , error);
throw error;
});
}
async function getUsersData ( ): Promise <void > {
try {
const users = await fetchUsers ();
console .log (users[0 ].name );
} catch (error) {
console .error ("处理错误:" , error);
}
}
getUsersData ();
14. 什么是索引签名(Index Signature)?它的作用是什么?请举例说明。 索引签名用于'定义动态属性的对象'(实习中处理键值对、配置对象常用),需讲清'定义''语法''例子':
索引签名定义 :当对象的'属性名不确定'(如属性名是动态生成的),但'属性名类型'和'属性值类型'固定时,用索引签名约束对象的键值类型,语法为 [key: KeyType]: ValueType。
核心作用 :约束动态属性的类型,避免因属性名不确定导致的类型错误(如遍历键值对时确保值的类型)。
实习常用例子 :
例子 1:定义'字符串键 -> 数字值'的映射对象(如用户分数表):
interface ScoreMap {
[key : string ]: number ;
}
const userScores : ScoreMap = {
"张三" : 90 ,
"李四" : 85 ,
"王五" : 95 ,
};
for (const username in userScores) {
const score = userScores[username];
console .log (`${username} : ${score} ` );
}
例子 2:混合固定属性和动态属性(索引签名可与固定属性共存):
interface UserConfig {
name : string ;
[key : string ]: string | number ;
}
const config : UserConfig = {
name : "张三" ,
age : 22 ,
address : "北京" ,
};
15. 如何在 TypeScript 中实现类的继承和多态?请举例说明。 类是 OOP 基础(实习中封装组件逻辑、工具类常用),需讲清'继承(extends)''多态(方法重写)''例子':
1. 类的继承(extends) :
语法:子类用 extends 继承父类,子类会继承父类的属性和方法(除 private 成员)。
关键:用 super() 在子类构造函数中调用父类构造函数。
2. 多态 :
定义:子类重写(override)父类的方法,使不同子类的同一方法有不同实现(需父类方法用 abstract 或普通方法,子类用 override 关键字显式重写)。
abstract class Animal {
protected name : string ;
constructor (name : string ) {
this .name = name;
}
abstract makeSound (): void ;
eat (): void {
console .log (`${this .name} 在吃东西` );
}
}
class Dog extends Animal {
constructor (name : string ) {
super (name);
}
override makeSound (): void {
console .log (`${this .name} 汪汪叫` );
}
fetch (): void {
console .log (`${this .name} 在捡球` );
}
}
class Cat extends Animal {
constructor (name : string ) {
super (name);
}
override makeSound (): void {
console .log (`${this .name} 喵喵叫` );
}
}
function letAnimalSound (animal : Animal ): void {
animal.makeSound ();
animal.eat ();
}
const dog = new Dog ("旺财" );
const cat = new Cat ("咪宝" );
letAnimalSound (dog);
letAnimalSound (cat);
dog.fetch ();
16. TypeScript 中的 readonly 和 const 的区别是什么?请举例说明。 两者均'只读',但作用对象不同(实习中易混淆),需对比'作用范围''修改限制''例子':
特性 readonlyconst作用对象 类的属性、接口的属性 变量(let/var 替换为 const) 作用阶段 编译阶段(TS 类型校验) 编译阶段(JS 语法层面) 修改限制 仅限制'属性重新赋值'(对象属性内部可改) 限制'变量重新赋值'(若变量是对象,内部属性可改) 使用场景 定义不可重新赋值的类/接口属性 定义不可重新赋值的变量
const age : number = 22 ;
age = 23 ;
const user : { id : string ; name : string } = {
id : "123" ,
name : "张三"
};
user.name = "李四" ;
user = { id : "456" , name : "王五" };
class User {
readonly id : string ;
name : string ;
constructor (id : string , name : string ) {
this .id = id;
this .name = name;
}
}
const user = new User ("123" , "张三" );
user.name = "李四" ;
user.id = "456" ;
17. 什么是条件类型(Conditional Types)?请举例说明它的使用场景。 条件类型是'基于条件推断类型'的语法(实习中写工具类型、复用类型常用),需讲清'定义''语法''例子':
条件类型定义 :类似 JS 的三元运算符(condition ? a : b),但作用于类型层面,根据'类型是否满足某个条件'动态生成类型,语法为 T extends U ? X : Y(若 T 是 U 的子类型,则为 X 类型,否则为 Y 类型)。
核心作用 :实现'类型的动态判断与生成',常用于封装通用工具类型(如'如果是数组,取数组元素类型;否则取原类型')。
实习常用例子 :
例子 1:定义'获取数组元素类型'的工具类型(若输入是数组,返回元素类型;否则返回原类型):
type ElementOf <T> = T extends Array <infer U> ? U : T;
type StrArr = string [];
type StrType = ElementOf <StrArr >;
type Num = number ;
type NumType = ElementOf <Num >;
interface User {
id : string ;
name : string ;
}
type ApiResponse = User [] | User ;
type FinalUserType = ElementOf <ApiResponse >;
例子 2:定义'排除 null/undefined'的工具类型:
type NonNullable <T> = T extends null | undefined ? never : T;
type MaybeUser = User | null | undefined ;
type SafeUser = NonNullable <MaybeUser >;
18. 如何在 TypeScript 中定义函数的类型?包括参数类型、返回值类型、可选参数、默认参数等。 函数类型是基础(实习中写工具函数、事件处理函数必用),需覆盖'函数声明''函数表达式''特殊参数':
function add (a : number , b : number ): number {
return a + b;
}
add (1 , 2 );
add ("1" , 2 );
type AddFunc = (a : number , b : number ) => number ;
const add : AddFunc = (a, b ) => {
return a + b;
};
3. 可选参数(参数后加 ?,需放在必选参数后) :
function greet (name : string , age ?: number ): string {
if (age) {
return `Hello, ${name} , you are ${age} years old` ;
}
return `Hello, ${name} ` ;
}
greet ("张三" );
greet ("李四" , 22 );
4. 默认参数(参数赋值默认值,自动变为可选参数) :
function greetWithDefault (name : string , age : number = 18 ): string {
return `Hello, ${name} , you are ${age} years old` ;
}
greetWithDefault ("张三" );
greetWithDefault ("李四" , 22 );
5. 剩余参数(用 ... 接收多个参数,类型为数组) :
function sum (...nums : number [] ): number {
return nums.reduce ((total, num ) => total + num, 0 );
}
sum (1 , 2 , 3 );
sum (1 , "2" );
19. TypeScript 的模块系统(import/export)与 JavaScript 的模块系统有什么区别? 模块系统是模块化开发基础(实习中拆分组件/工具类常用),需讲清'TS 对 JS 模块的扩展':
1. 核心共同点 :
均支持 ES 模块(import/export)和 CommonJS 模块(require/module.exports),语法一致。
均通过模块隔离代码(避免全局变量污染)。
2. TS 模块系统的扩展(核心区别) :
支持'类型导出/导入' :
JS 仅能导出/导入'值'(变量、函数、对象等);
TS 可额外导出/导入'类型'(接口、类型别名、泛型等),用 export type/import type 语法(明确区分类型和值,优化编译)。
模块解析增强 :
导入时可简化路径:import { User } from "@/types"(无需写相对路径 ../types)。
类型检查 :
TS 导入模块时会检查'导入的类型是否存在'(如导入不存在的类型会报错);
JS 仅在运行时才会发现导入错误(如导入不存在的函数)。
TS 支持更灵活的模块解析(如 baseUrl、paths 配置,可简化导入路径),需在 tsconfig.json 中配置:
{
"compilerOptions" : {
"baseUrl" : "./src" ,
"paths" : {
"@/*" : [ "*" ]
}
}
}
export interface User {
name : string ;
}
export type ID = string | number ;
export const getUserName = (user : User ) => user.name ;
import { User , ID , getUserName } from "./types" ;
import type { User as ImportedUser } from "./types" ;
20. 实习中使用 TypeScript 时,你遇到过'类型'X'上不存在属性'Y''的错误吗?如何解决? 这是实习中最常见的 TS 错误,需讲清'常见原因''对应解决方法''例子',体现实战能力:
常见原因及解决方法 (按实习高频度排序):
原因 1:类型定义不完整(对象缺少该属性) :
解决:补充类型定义(如接口/类型别名中添加该属性)。
原因 2:类型推断错误(TS 推断的类型与实际类型不符) :
解决:用类型断言指定实际类型,或显式标注变量类型。
原因 3:使用 any/unknown 类型未处理 :
解决:any 改为更具体的类型;unknown 需先断言为具体类型。
原因 4:联合类型未缩小范围 :
解决:用类型守卫缩小联合类型范围,确保访问的属性在当前类型中存在。
type UserOrStr = User | string ;
const value : UserOrStr = {
name : "张三" ,
age : 22
};
console .log (value.name );
if (typeof value !== "string" ) {
console .log (value.name );
}
let data : any = {
name : "张三" ,
age : 22
};
data = "hello" ;
console .log (data.age );
interface User {
name : string ;
age : number ;
}
let data : User = {
name : "张三" ,
age : 22
};
data = "hello" ;
const input = document .getElementById ("username" );
console .log (input.value );
const input = document .getElementById ("username" ) as HTMLInputElement ;
console .log (input.value );
interface User {
name : string ;
}
const user : User = {
name : "张三" ,
age : 22
};
console .log (user.age );
interface User {
name : string ;
age : number ;
}
const user : User = {
name : "张三" ,
age : 22
};
console .log (user.age );
21. 什么是映射类型(Mapped Types)?常用的内置映射类型有哪些?请举例说明。 映射类型是'批量创建类型'的语法(实习中复用类型、修改类型常用),需讲清'定义''内置类型''例子':
映射类型定义 :基于已有类型的'属性键'批量生成新类型,语法为 { [P in K]: T }(P 是属性键占位符,K 是属性键集合,T 是属性值类型),核心是'遍历已有类型的属性,修改属性的类型或特性'。
常用内置映射类型 (TS 内置,实习高频):
**Partial<T>**:将 T 的所有属性变为可选属性。
场景:定义'部分更新'的参数类型(如 API PATCH 请求,仅传需要修改的属性)。
**Required<T>**:将 T 的所有属性变为必选属性(与 Partial 相反)。
**Readonly<T>**:将 T 的所有属性变为只读属性。
**Pick<T, K>**:从 T 中'挑选'指定属性 K 组成新类型(K 必须是 T 的属性键)。
场景:提取类型中的部分属性(如从 User 中提取'姓名和年龄')。
**Omit<T, K>**:从 T 中'排除'指定属性 K 组成新类型(与 Pick 相反)。
type UserWithoutAddress = Omit <User , "address" >;
interface User {
name : string ;
age : number ;
address : string ;
}
type UserNameAndAge = Pick <User , "name" | "age" >;
interface Config {
apiUrl : string ;
timeout : number ;
}
type ReadonlyConfig = Readonly <Config >;
const config : ReadonlyConfig = {
apiUrl : "https://api.com" ,
timeout : 5000
};
config.apiUrl = "https://new-api.com" ;
interface PartialUser {
name ?: string ;
age ?: number ;
}
type RequiredUser = Required <PartialUser >;
interface User {
name : string ;
age : number ;
address : string ;
}
type PartialUser = Partial <User >;
function updateUser (user : PartialUser ): void {
}
updateUser ({ name : "李四" });
22. 如何在 TypeScript 中处理 DOM 元素的类型?请举例说明常见 DOM 元素类型的使用。 操作 DOM 是实习必备(如表单、按钮交互),需讲清'常用 DOM 类型''类型断言''例子':
核心原则 :TS 为不同 DOM 元素提供了专属类型(如 HTMLInputElement、HTMLDivElement),需明确元素类型才能安全调用其属性/方法(避免 HTMLElement 类型的限制)。
常用 DOM 元素类型及例子 :
**HTMLElement**:所有 HTML 元素的基类(包含通用属性,如 id、className、style),但无元素专属属性(如 value、src)。
**HTMLInputElement**:输入框元素类型(含专属属性 value、checked,方法 focus())。
**HTMLButtonElement**:按钮元素类型(含专属方法 click())。
**HTMLSelectElement**:下拉选择框类型(含专属属性 value、options)。
关键注意事项 :
若无法确定元素是否存在(如动态渲染的元素),需先判断非 null,再断言类型:
const input = document .getElementById ("username" );
if (input) {
const typedInput = input as HTMLInputElement ;
typedInput.value = "test" ;
}
const citySelect = document .getElementById ("city" ) as HTMLSelectElement ;
console .log ("选中城市:" , citySelect.value );
const submitBtn = document .getElementById ("submit" ) as HTMLButtonElement ;
submitBtn.addEventListener ("click" , () => {
console .log ("按钮被点击" );
});
submitBtn.click ();
const usernameInput = document .getElementById ("username" ) as HTMLInputElement ;
usernameInput.value = "[email protected] " ;
usernameInput.focus ();
例子:获取 div 元素(无专属属性,用 HTMLElement):
const div = document .getElementById ("container" ) as HTMLElement ;
div.className = "active" ;
23. 什么是 TypeScript 的'严格模式'(strict: true)?开启后会启用哪些关键规则? 严格模式是 TS 类型安全的核心(公司项目普遍开启),需讲清'定义''关键规则''作用':
严格模式定义 :在 tsconfig.json 中设置 compilerOptions.strict: true,开启后会启用一组'严格的类型检查规则',强制开发者编写更严谨的类型代码,减少潜在的类型错误(是 TS 推荐的最佳实践)。
开启后启用的关键规则 (实习高频,需重点说明):
**noImplicitAny**:禁止隐式 any 类型(变量/参数未指定类型且 TS 无法推断时,报错)。
作用:避免因 any 丢失类型检查(如函数参数未指定类型,TS 不再默认视为 any)。
**strictNullChecks**:严格检查 null 和 undefined(null/undefined 不能赋值给其他类型,除非显式包含)。
作用:避免'空指针错误'(如访问 null.value),是最实用的规则之一。
**strictFunctionTypes**:严格检查函数参数类型(函数参数类型必须精确匹配,不能是父类型)。
**strictPropertyInitialization**:严格检查类属性的初始化(类的非可选属性必须在构造函数中初始化,或用 ! 显式标记为'后续初始化')。
实习建议 :开发时务必开启严格模式(即使是个人项目),养成严谨的类型习惯,面试中提及'熟悉严格模式及关键规则'会加分。
class User {
name : string ;
age ?: number ;
constructor (name : string ) {
this .name = name;
}
}
class BadUser {
name : string ;
constructor ( ) {}
}
type AddFunc = (a : number , b : number ) => number ;
const add : AddFunc = (a : number , b : number ) => a + b;
const badAdd : AddFunc = (a : number , b : any ) => a + b;
let name : string = null ;
let name : string | null = null ;
if (name) {
console .log (name.length );
}
function add (a, b ) {
return a + b;
}
function add (a : number , b : number ): number {
return a + b;
}
24. 如何在 TypeScript 中实现泛型约束(Generic Constraints)?请举例说明它的作用。 泛型约束是'限制泛型范围'的语法(避免泛型过于宽泛,确保有特定属性/方法),需讲清'定义''语法''例子':
泛型约束定义 :默认情况下,泛型 T 可以是任意类型,若需限制 T 必须包含某个属性/方法(如'T 必须有 length 属性'),需用 T extends U 语法(U 是约束类型,T 必须是 U 的子类型),这就是泛型约束。
核心作用 :避免泛型过于灵活导致的错误(如调用泛型变量的 length 方法,但 T 可能是 number 类型,无 length),确保泛型变量有所需的属性/方法。
实习常用例子 :
例子 1:定义'获取有 length 属性的变量的长度'的泛型函数(约束 T 必须有 length 属性):
interface HasLength {
length : number ;
}
function getLength<T extends HasLength >(value : T): number {
return value.length ;
}
getLength ("hello" );
getLength ([1 , 2 , 3 ]);
getLength ({ length : 10 });
例子 2:定义'获取对象指定属性值'的泛型函数(约束'属性必须是对象的键'):
function getProperty<T, K extends keyof T>(obj : T, key : K): T[K] {
return obj[key];
}
const user = {
name : "张三" ,
age : 22
};
getProperty (user, "name" );
getProperty (user, "age" );
25. 实习中使用 TypeScript 时,你遇到过哪些印象深刻的问题?是如何解决的?(项目经验类,必问) 这类问题考察实战能力,需结合实习场景(如 API 类型、第三方库、DOM 操作),讲清'问题场景''解决过程''收获',避免空泛:
同时,对于第三方库的类型问题,优先使用官方声明文件,若无则通过自定义声明文件补充,这既保证了类型安全,也提升了开发效率。这些经历让我更深刻地理解了 TypeScript'类型即契约'的设计理念,在后续开发中会更注重类型定义的准确性和前瞻性。
26. TypeScript 中的'类型推断'是指什么?请举例说明常见的类型推断场景。 类型推断是 TS 的核心特性(减少手动类型标注),需讲清'定义''场景''例子':
类型推断定义 :TS 在未显式指定类型时,自动根据变量的初始化值、函数的返回值等信息推断出变量/参数/返回值的类型,简化代码的同时保持类型安全。
常见类型推断场景 :
function identity<T>(value : T): T {
return value;
}
const str = identity ("hello" );
const num = identity (123 );
type LogFunc = (message : string ) => void ;
const log : LogFunc = (msg ) => {
console .log (msg);
};
const fruits = ["apple" , "banana" , "orange" ];
const user = {
name : "张三" ,
age : 22
};
function add (a : number , b : number ) {
return a + b;
}
let username = "张三" ;
let age = 22 ;
let isStudent = true ;
27. 如何在 TypeScript 中定义'函数重载'?请举例说明其使用场景。 函数重载用于'同一函数支持多种参数类型/个数'(实习中处理灵活入参常用),需讲清'定义''语法''例子':
函数重载定义 :允许为同一函数定义多个'函数签名'(不同的参数类型/个数/返回值类型),再提供一个统一的实现,使函数能根据不同入参自动匹配对应的签名,提升类型准确性。
语法规则 :
先定义多个函数签名(仅声明参数和返回值类型,无实现);
最后定义一个'实现函数'(参数类型需兼容所有签名,通常用联合类型)。
方式 1:传入 Date 对象,返回默认格式(YYYY-MM-DD);
方式 2:传入 Date 对象 + 自定义格式字符串,返回自定义格式。
关键注意事项 :
实现函数的参数类型必须'兼容所有签名'(如例子中 format 为可选参数,兼容签名 1 的无参数和签名 2 的有参数);
调用函数时,TS 会根据入参匹配最精确的签名,提供对应类型提示。
实习常用场景(处理多种入参格式) :
例子:定义'格式化日期'的函数,支持两种调用方式:
function formatDate (date : Date ): string ;
function formatDate (date : Date , format : string ): string ;
function formatDate (date : Date , format ?: string ): string {
if (format) {
return `自定义格式:${date.toISOString()} ${format} ` ;
} else {
return date.toISOString ().split ("T" )[0 ];
}
}
const defaultFormatted = formatDate (new Date ());
const customFormatted = formatDate (new Date (), "YYYY/MM/DD" );
28. TypeScript 中的'命名空间(Namespace)'是什么?它与模块(Module)的区别是什么? 命名空间用于'组织代码、避免命名冲突'(实习中较少用,但需了解),需讲清'定义''区别':
实习建议 :现代前端项目(如 Vue/React+TS)中,优先使用模块(import/export)组织代码,命名空间仅在维护旧项目时可能用到。
特性 命名空间(Namespace) 模块(Module) 文件关联 可在单个文件中定义,支持跨文件合并 每个文件就是一个独立模块 依赖管理 无内置依赖管理,需手动通过 <reference> 引入 支持 import/export 管理依赖 作用域 全局作用域内的一个容器(可能污染全局) 独立作用域(文件内声明默认不暴露) 现代项目适用性 适用于早期非模块化项目(如全局脚本) 适用于现代模块化项目(推荐使用)
命名空间定义 :用 namespace 关键字定义,用于将相关的类型、函数、变量等封装在一个命名空间内,通过 export 暴露内部成员,避免全局命名冲突,语法:
namespace MathUtils {
export function add (a : number , b : number ): number {
return a + b;
}
export function multiply (a : number , b : number ): number {
return a * b;
}
function subtract (a : number , b : number ): number {
return a - b;
}
}
MathUtils .add (1 , 2 );
MathUtils .multiply (2 , 3 );
29. 如何在 TypeScript 中定义'抽象类(Abstract Class)'和'抽象方法(Abstract Method)'?它们的作用是什么? 抽象类是'不能实例化的基类'(用于定义子类的公共接口),需讲清'定义''作用''例子':
抽象类与抽象方法定义 :
抽象类:用 abstract 关键字修饰的类,不能直接实例化 ,仅用于被继承。
抽象方法:抽象类中用 abstract 关键字修饰的方法,只有声明、没有实现 ,子类必须重写该方法。
核心作用 :
定义'类的模板'(规定子类必须实现的方法),确保子类结构一致;
封装子类的公共逻辑(抽象类可包含普通方法和属性,子类继承后直接使用)。
关键注意事项 :
抽象类不能直接实例化(new BaseComponent("test") 会报错);
子类必须重写所有抽象方法(否则子类也需声明为抽象类)。
abstract class BaseComponent {
protected name : string ;
constructor (name : string ) {
this .name = name;
}
public logName (): void {
console .log (`组件名称:${this .name} ` );
}
public abstract render (): string ;
public abstract update (data : unknown ): boolean ;
}
class ButtonComponent extends BaseComponent {
constructor (name : string ) {
super (name);
}
public override render (): string {
return `<button>${this .name} </button>` ;
}
public override update (data : { text : string }): boolean {
this .name = data.text ;
return true ;
}
}
const button = new ButtonComponent ("提交按钮" );
button.logName ();
console .log (button.render ());
button.update ({ text : "确认按钮" });
30. TypeScript 中,如何处理'循环依赖'问题?请举例说明常见解决方案。 循环依赖指'模块 A 依赖模块 B,模块 B 同时依赖模块 A'(实习中拆分模块时易出现),需讲清'问题表现''解决方案':
问题表现 :
编译时可能报错'Cannot access 'XXX' before initialization';
运行时可能出现变量/类型未定义(因模块加载顺序冲突)。
常见解决方案 (按实习实用性排序):
提取公共类型到独立模块 (最推荐):
场景:A 和 B 因共享类型相互依赖,将共享类型提取到 types.ts,A 和 B 均依赖 types.ts,消除直接依赖。
使用'延迟导入'(动态 import) :
场景:仅在函数内部使用依赖,通过动态 import() 延迟加载,避免模块初始化阶段的依赖冲突。
调整模块设计,消除不必要依赖 :
分析依赖关系,合并或拆分模块,确保依赖链是单向的(如 A→B→C,而非 A↔B)。
import { funcB } from "./B" ;
export function funcA ( ) {
return funcB ();
}
export async function funcB ( ) {
const { funcA } = await import ("./A" );
return funcA () + " from B" ;
}
export interface User {
id : string ;
name : string ;
}
import { User } from "./types" ;
export function formatUser (user : User ): string {
return `${user.id} : ${user.name} ` ;
}
import { User } from "./types" ;
export function createUser (name : string ): User {
return {
id : Date .now ().toString (),
name
};
}
31. 什么是'字面量类型(Literal Types)'?它与普通类型有什么区别?请举例说明。 字面量类型是'具体值的类型'(比普通类型更精确),需讲清'定义''类型''例子':
字面量类型定义 :表示'具体的单一值'的类型(如 10、"hello"、true),而普通类型(如 number、string)表示'一类值'。例如,10 是字面量类型(仅匹配值 10),number 是普通类型(匹配所有数字)。
常见字面量类型 :
字符串字面量类型 :如 "primary"、"success"(仅匹配特定字符串)。
数字字面量类型 :如 1、2(仅匹配特定数字)。
布尔字面量类型 :true 或 false(仅匹配特定布尔值)。
实习常用场景 :定义固定选项(如按钮类型、状态码、性别等),比枚举更轻量,常用于联合类型中限制取值范围。
let str : string = "primary" ;
str = "success" ;
let primaryStr : "primary" = "primary" ;
primaryStr = "success" ;
type ButtonType = "primary" | "secondary" | "danger" ;
const btnType : ButtonType = "primary" ;
const invalidBtnType : ButtonType = "warning" ;
type StatusCode = 200 | 400 | 500 ;
const code : StatusCode = 200 ;
const errorCode : StatusCode = 404 ;
32. TypeScript 中,infer 关键字的作用是什么?请举例说明其使用场景。 infer 用于'在条件类型中推断类型'(高级特性,实习中写工具类型常用),需讲清'定义''例子':
infer 作用 :在条件类型(T extends ... ? X : Y)中,用于'推断出一个类型变量'(类似函数中的参数),并在条件分支中使用该推断出的类型,简化类型提取逻辑。
实习常用场景(提取类型中的部分信息) :
例子 1:提取函数的返回值类型:
type ReturnType <T> = T extends (...args : any []) => infer R ? R : T;
function getUser ( ) {
return {
id : "123" ,
name : "张三"
};
}
type UserReturnType = ReturnType <typeof getUser>;
type ElementType <T> = T extends (infer U)[] ? U : T;
type StrArr = string [];
type StrElement = ElementType <StrArr >;
type Num = number ;
type NumElement = ElementType <Num >;
例子 3:提取 Promise 的 resolve 类型:
type PromiseType <T> = T extends Promise <infer U> ? U : T;
type UserPromise = Promise <{ id : string ; name : string }>;
type User = PromiseType <UserPromise >;
33. 如何在 TypeScript 中定义'全局变量'的类型?请举例说明。 全局变量(如 window 上的自定义属性)的类型定义是实习常见需求,需讲清'声明方式':
核心方法 :通过'全局声明'(在 .d.ts 文件中扩展全局命名空间),告知 TS 全局变量的类型。
实习常用例子 :
场景 1:为 window 对象添加自定义属性 appConfig:
declare global {
interface Window {
appConfig ?: {
apiUrl : string ;
env : "development" | "production" ;
};
}
}
if (window .appConfig ) {
console .log ("API 地址:" , window .appConfig .apiUrl );
if (window .appConfig .env === "development" ) {
console .log ("开发环境" );
}
}
场景 2:定义全局变量 VERSION(如构建时注入的版本信息):
declare global {
interface Window {
VERSION : string ;
}
}
console .log (window .VERSION );
注意 :声明文件需放在 tsconfig.json 的 include 路径中,或添加 /// <reference path="..." /> 引入。
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown 转 HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
HTML 转 Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online