TypeScript知识点汇总及相关面试题

文章目录

一、TS介绍

1.什么是TypeScript?

TypeScript (简称:TS),是 JavaScript(JS)的超集 ,可以理解为:TypeScript = JavaScript + 类型系统(类型约束)

2. 为什么必须学TS?

JavaScript 是**「弱类型/动态类型」语言,写代码时不检查变量类型**,这是JS最大的痛点,也是 前端 项目中80%的BUG来源(比如数字和字符串相加、传参类型错误、调用不存在的方法)。

TypeScript 完美解决这个问题,核心优势

  • 提前报错,规避BUG :写代码时(编译阶段)就发现类型错误,不用等到运行时才出问题,尤其适合团队开发/大型项目;
  • 语法提示,开发提速VSCode会根据类型 给出精准的代码提示、属性联想,不用记API,写代码效率提升50%;
  • 代码易读,便于维护变量/函数的类型一目了然,别人看你的代码能快速理解,自己写的代码隔月也能看懂;

3.TS和JS的区别

  • 类型系统TS 有静态类型编译阶段检查类型 ,变量声明时需指定/推断类型);JS 是动态类型运行时才确定类型,易出现类型错误)。
  • 编译阶段TS 需通过编译器(如 tsc)编译为 JS 才能运行(编译时会做类型校验);JS 可直接在浏览器/Node 环境运行。
  • 特性补充TS 新增接口(Interface)、泛型(Generic)、枚举(Enum)、类型守卫等特性;JS 无这些原生类型相关特性。
  • 开发体验 :TS 支持 工具智能提示、自动补全、类型错误提前预警;JS 开发时需手动判断类型,错误难提前发现。

二、TS搭建及运行

1.TS的运行逻辑

浏览器/Node.js 能识别JS代码不能 直接运行TS代码,TS的运行分两步(核心流程,记牢):

  • 写代码 :在 .ts 后缀的文件中编写TypeScript代码;
  • 编译转换 :通过 TS编译器(tsc),把 .ts 文件 编译成浏览器能识别的 .js 文件
  • 运行代码:执行编译后的 .js 文件即可。
bash 复制代码
 # 1. 全局安装TS编译器(电脑装一次,永久使用,Node.js环境下执行)
npm install -g typescript

# 2. 编译TS文件为JS文件(比如:把index.ts编译成index.js)
tsc index.ts
# 查看版本
tsc -v

# 3. 运行编译后的JS文件
node index.js

2.TS的编译配置文件 tsconfig.json

1. 生成配置文件

bash 复制代码
# 在项目根目录执行,自动生成tsconfig.json
tsc --init

2. 必改的3个核心配置

打开生成的tsconfig.json,找到以下配置,修改为对应值,其余默认即可:

json 复制代码
{
  "target": "ES6",        // 编译后的JS版本为ES6,兼容所有浏览器/Node.js
  "module": "ES6",        // 模块规范为ES6,支持import/export
  "strict": true,         // 开启严格模式(重中之重!),强制检查所有类型错误,杜绝隐藏BUG
}

3. 一键编译整个项目

bash 复制代码
# 执行后,TS会根据tsconfig.json的配置,自动编译项目中所有.ts文件为.js文件
tsc

3.如何将 TypeScript 集成到 Vue 项目中?(实习高频,以 Vue3 + Vite 为例)

1⃣️、初始化 Vue + TS 项目(Vite 方式,最常用):

bash 复制代码
# 1. 执行创建命令,选择 Vue + TypeScript
npm create vite@latest my-vue-ts-project -- --template vue-ts
# 2. 安装依赖并启动
cd my-vue-ts-project
npm install
npm run dev

2⃣️、核心配置文件

  • tsconfig.jsonTS 编译配置 ,核心字段:
    • compilerOptions.strict: true:开启严格模式(强制类型校验,推荐开启)。
    • compilerOptions.lib: ["ESNext", "DOM"]指定依赖的库(支持 ES 新特性和 DOM API)。
    • compilerOptions.types: ["vite/client"]引入 Vite 客户端类型(支持 import.meta.env 等)。
  • vite.config.tsVite 配置文件(用 TS 编写,需导出 defineConfig 对象)。

3⃣️、组件中使用

html 复制代码
<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>

三、TS知识点详解

1. TS数据类型

1.1.变量声明语法

  • 声明语法: 常量名: 数据类型 (类型注解的冒号后必须加空格
  • 变量的类型一旦确定,就永远不能改变
  • TS中声明变量,优先用const,其次用let,完全抛弃var(var无块级作用域,目前已彻底废弃);
  • const声明的常量,值不能修改,类型也不能修改,是最安全的声明方式;
  • 变量名命名规范:小驼峰 userName常量名全大写 MAX_NUM
javascript 复制代码
// TS变量声明语法:比JS多了一个 冒号+类型
let 变量名: 数据类型 = 变量值;
const 常量名: 数据类型 = 常量值;

1.2.基本类型:string、number、boolean、symbol、bigint、null、undefined

注意: null 和 undefined 两个类型一旦赋值上,就不能在赋值给任何其他类型

javascript 复制代码
    let str: string = "Domesy"  //字符串
    let num: number = 7   // 数字
    let bool: boolean = true  //布尔
    let sym: symbol = Symbol();   //symbol
    let big: bigint = 10n    //bigint
    let nu: null = null    //null
    let un: undefined = undefined   //undefined

1.3.引用类型:array、 Tuple(元组)、 object(包含Object和{})、function

  • array :声明时写法数据类型 []Array<数据类型>
  • Tuple :是 Array 的一种特殊情况。固定长度、固定类型的数组
  • 对象:
    • object: 非原始类型。使用{}定义 {属性名:数据类型;...}(直接:object,修改对象属性会出错,因为未设置属性类型)
    • Object: 所有的原始类型或非原始类型 都可以进行赋值 ,除了null和undefined
  • function:
    • 声明时写法function箭头函数。返回值的类型可以写,写:必须要有对应类型的返回值;但通常情况下是省略不写,因为TS的类型推断功能够正确推断出返回值类型 。
    • 参数:
      • 可选参数: ? 配置可有可无的参数。 age?: number
      • 默认参数: = 设定默认值。age: number = 11
      • 剩余参数: ...,如...numbers: number[]
javascript 复制代码
//array
let arr1: number[] = [1, 2, 3] 
let arr2: Array<number> = [1, 2, 3] 
let arr2: Array<number> = [1, 2, '3'] // error 
 //要想是数字类型或字符串类型,需要使用 |
let arr3: Array<number | string> = [1, 2, '3'] //ok

//Tuple:固定了元素的类型和个数
let t: [number, string] = [1, '2'] // ok
let t1: [number, string] = [1, 3] // error
let t2: [number, string] = [1] // error

//object(小写)
let obj1: object = { a: 1, b: 2}
obj1.a = 3 // error
let obj2: { a: number, b: number } = {a: 1, b: 2}
obj2.a = 3 // ok

//Object(大写)
let obj: Object;
    obj = 1; // ok
    obj = {}; // ok
    obj = 10n //ok
    obj = null; // error
    obj = undefined; // error

//function
function setName1(name: string) { //ok
  console.log("hello", name);
}
function setName2(name: string):string { //error
  console.log("hello", name);
}
function setName4(name: string): string { //ok
  console.log("hello", name);
  return name
}
//箭头函数,返回值写法同上
const setName5 = (name:string) => console.log("hello", name);
//参数
// 可选参数
const setInfo1 = (name: string, age?: number) => console.log(name, age)
setInfo1('Domesy') //"Domesy",  undefined
setInfo1('Domesy', 7) //"Domesy",  7
// 默认参数
const setInfo2 = (name: string, age: number = 11) => console.log(name, age)
setInfo2('Domesy') //"Domesy",  11
setInfo2('Domesy', 7) //"Domesy",  7
// 剩余参数
const allCount = (...numbers: number[]) => console.log(`数字总和为:${numbers.reduce((val, item) => (val += item), 0)}`)
allCount(1, 2, 3) //"数字总和为:6"

1.4.特殊类型:any、unknow、void、nerver、Enum(枚举)

  • 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") }
  • Enum: 可以定义一些带名字的常量 。枚举的类型只能是 string 或 number,定义的名称不能为关键字。数字:不定义属性值,默认从0递增;定义属性值,从值递增
javascript 复制代码
//any
let d:any; //等价于 let d 
d = '1';
d = 2;

//unknow
let u:unknown;
u = true; //ok
u = [1, 2, 3]; //ok
let value1: any = a //ok
let value2: unknown = u //ok
let value3: string = u //error
let value4: number = u //error
u.set() // error
u() // error

//void
const setInfo2 = ():void => { return 2 } // error
const setInfo3 = ():void => { return true } // error
const setInfo4 = ():void => { return  } // ok
const setInfo5 = ():void => { return undefined } //ok

//never  当抛出异常的情况和无限死循环
let error = ():never => { // 等价约 let error = () => {}
        throw new Error("error");
};

let error1 = ():never => {
    while(true){}
}

//enum  不定义属性值,默认从0递增;定义属性值,从值递增
//number类型
enum Status {
    up,        // 0   若up=8,从8开始递增
    dowm,      // 1    9
}
//string类型
enum Status {
    up = 'up',      
    dowm = 'dowm'    
}
//定义常量
enum Directions {
 Up,
 Down,
 Left,
 Right
}
let x = Directions.Up;

1.5.其他类型:字面量类型、交叉类型(&)、联合类型(|)、类型别名 (type)、类型推论(TS自己判断)、类型断言 (as,人为告诉)

  • 字面量类型: 限制值为特定字面量,目前支持字符串、数字、布尔三种类型。 let num: 1 | 2 | 3即num值只能是1/2/3
  • 交叉类型:
    • 定义 :表示变量的类型是"多个类型的合并 ",包含所有类型的属性/方法 ,用 & 分隔类型,核心是""的关系。
    • 语法TypeA & TypeB & TypeC
    • 实习场景 :处理"合并多个类型"的情况(如合并基础用户信息和权限信息)。
  • 联合类型(|
    • 定义 :表示变量的类型是"多个类型中的任意一个 ",用 | 分隔类型,核心是""的关系。
    • 语法TypeA | TypeB | TypeC
    • 实习场景 :处理"参数可能有多种类型"的情况(如函数参数支持 string 或 number)。
  • 类型别名 (type):type 给「复杂类型」起一个别名 ,后续直接用别名代替原类型,一次定义,全局复用type 类型别名 = 具体类型;
  • 类型推论: 在TS中如果不设置类型,并且不进行赋值时,将会推论为any类型,如果进行赋值就会默认为类型
  • 类型断言 (as): 告诉TS:这个变量就是XX类型。变量 as 目标类型
javascript 复制代码
// 类型推论
let a; // 推断为any
let str = '小杜杜'; // 推断为string
let num = 13; // 推断为number
str = true // error Type 'boolean' is not assignable to type 'string'.(2322)
num = 'Domesy' // error

//字面量类型
let str:'小杜杜' 
let num: 1 | 2 | 3 = 1
let flag:true

str = '小杜杜' //ok
str = 'Donmesy' // error

num = 2 //ok
num = 7 // error

flag = true // ok
flag = false // error

//交叉类型
type AProps = { a: string }
type BProps = { b: number }
type allProps = AProps & BProps
const Info: allProps = {
    a: '小杜杜',
    b: 7
}

// 联合类型 |
let value: number | string;
value = 10;  // ✅ 正确
value = '10';// ✅ 正确
value = true;// ❌ 错误:只能是数字或字符串

//类型别名 type
type NumOrStr = number | string;
// 后续直接用别名,不用重复写 number | string
let value: NumOrStr = 10;
let value2: NumOrStr = '10';

//类型断言
// ✅ 经典案例:获取DOM元素(前端开发必写)
// TS默认不知道 document.getElementById('box') 是什么类型的DOM,此时用断言告诉TS是HTMLDivElement
const box = document.getElementById('box') as HTMLDivElement;
// ✅ 断言后,就能正常调用div的属性/方法,不报错
box.style.width = '100px';

// ✅ 案例2:联合类型的断言
let value: number | string = 'hello';
// 明确知道value是字符串,断言为string,就能调用字符串的方法
let len = (value as string).length;

参考

原文链接:https://blog.csdn.net/student66666666/article/details/156420409

原文链接:https://blog.csdn.net/2301_79847249/article/details/142494651

1.3.什么是类型守卫(Type Guard)?

类型守卫定义 :在运行时检查变量的类型 ,让 TS 在代码块内部自动推断出变量的具体类型(缩小类型范围,避免类型错误)。

常用类型守卫方式

  • typeof 守卫 (判断基础类型string/number/boolean/symboltypeof value === "string"
  • instanceof 守卫 (判断引用类型:数组、类实例 等)data instanceof Array
  • in 守卫 (判断对象是否包含某个属性key in obj
  • is守卫 (通过函数返回 param is Type 语法 自定义)function isUser(data: unknown): data is User
javascript 复制代码
//typeof 守卫
type StrOrNum = string | number;
function getLength(value: StrOrNum): number {
  if (typeof value === "string") {
    return value.length; // TS 推断 value 为 string(可调用 length)
  } else {
    return value.toString().length; // TS 推断 value 为 number
  }
}

//instanceof 守卫
type ArrOrObj = number[] | { value: number };
function getValue(data: ArrOrObj): number {
  if (data instanceof Array) {
    return data[0]; // TS 推断 data 为 number[]
  } else {
    return data.value; // TS 推断 data 为 { value: number }
  }
}

//in 守卫
interface Dog {
  bark: () => void;
}

interface Cat {
  meow: () => void;
}

function makeSound(animal: Dog | Cat): void {
  if ("bark" in animal) {
    animal.bark(); // TS 推断 animal 为 Dog
  } else {
    animal.meow(); // TS 推断 animal 为 Cat
  }
}

//自定义类型守卫(通过函数返回 param is Type 语法自定义)
interface User {
  id: string;
  name: string;
}

// 自定义守卫:判断是否为 User 类型
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); // TS 推断 apiData 为 User
}

2. 面向对象

程序中所有操作都需要通过对象来完成。一切皆对象

比如:人这个对象,人姓名、年龄等数据--对象中的属性;人吃饭、睡觉等功能--对象中的方法

2.1. Class(类)

类 = 对象模型,创建对象,即定义类。类=属性+方法

  • class类中定义:
    • 构造函数constructor:class声明类,constructor声明构造函数。new 生成实例时会调用构造函数。
    • 修饰符:
      • static :关键词static开头的属性/方法,称为静态属性/方法,也叫类属性/方法。
      • readonly :将属性设置为只读 的。 只读属性必须 在声明时或构造函数里被初始化
      • public任意位置都可访问修改,默认值
      • private私有属性 。只能在类中修改 。需要在类中添加方法让外部访问
      • protected受保护的属性 。只能在类、子类内中访问
    • 存取器getters/setters:截取对对象成员的访问
  • extends 继承 :使用extends继承父类,子类中使用super 调用父类的构造函数和方法。
  • abstract 抽象 :用abstract关键字声明的类叫做抽象类声明的方法叫做抽象方法。
    • 抽象类的作用
      • 不能被直接实例化 ‌:只能通过继承由子类实例化
      • 作为基类使用‌:为多个子类提供公共属性、方法实现或强制实现特定接口。
      • 支持部分实现‌:可包含具体方法(有实现)和抽象方法(无实现,必须由子类实现)。
    • 抽象方法 的特点‌
      • 必须声明在抽象类中‌。
      • 只有声明,没有方法体‌(即无 {} 实现)。
      • 子类必须实现所有抽象方法‌,否则子类也需声明为 abstract。

1⃣️、class定义

javascript 复制代码
class Info {
  //静态属性
  static name1: string = 'Domesy'
  //成员属性,实际上是通过public上进行修饰,只是省略了
  nmae2:string = 'Hello' //ok 
  name3:string //error
  name4!:string //ok 不设置默认值的时候必须加入 !

  //构造方法
  constructor(_name:string){
    this.name4 = _name
  }

  //静态方法
  static getName = () => {
    return '我是静态方法'
  }

  //成员方法
  getName4 = () => {
    return `我是成员方法:${this.name4}`
  }

  //get 方法
  get name5(){
    return this.name4
  }

  //set 方法
  set name5(name5){
    this.name4 = name5
  }
}

const setName = new Info('你好')
console.log(Info.name1) //  "Domesy" 
console.log(Info.getName()) // "我是静态方法" 
console.log(setName.getName4()) // "我是成员方法:你好" 

2⃣️、继承extends

javascript 复制代码
class Animal {
  type: string;
  constructor(_type: string) {
    this.type = _type;
  }
  intro() {
    console.log(`I am a ${this.type}`);
  }
}

class Dog extends Animal {
  constructor(type: string,age: number) {
  	//属性的继承
    super(type);
    // 属性的扩展
    this.age = age
  }
  intro() {
  	//方法的继承
    super.intro()
  },
  // 方法的扩展
  sayHi() {
    console.log("wang! wang!");
  }
}

const dog = new Dog("dog");
dog.intro(); // I am a dog
dog.sayHi(); // wang wang

3⃣️、抽象类 abstract

javascript 复制代码
abstract class Animal {
  abstract makeSound(): void;
  sayHi(): void {
    console.log("Hi.");
  }
}

// error
class Dog extends Animal {
  // error 非抽象类"Dog"不会实现继承自"Animal"类的抽象成员"makeSound"
  // 必须要对抽象类中的抽象方法进行实现,属性也是一样
}

// good
class Cat extends Animal {
  makeSound() :void{
    console.log('miao miao~');
  }
}
const cat = new Cat();
cat.sayHi(); // hi
cat.makeSound(); // miao miao~

2.2. 接口interface

核心作用 的是"描述对象的结构 "------明确规定 一个对象必须有哪些属性、属性是什么类型。

特性:

  • 可选属性 :用?标记属性 ,表示该属性"可有可无",适合对象属性不固定的场景
  • 只读属性 :用readonly标记属性,表示该属性仅能在对象初始化时赋值后续无法修改,适合ID、创建时间等固定属性。
  • 索引签名 :对象的属性名不固定 (比如根据后端返回动态生成的 键值对 ),这时用"索引签名"就能约束动态属性的键和值的类型 ,避免乱加属性。
    • 语法:[key: 键类型]: 值类型,键类型通常是string或number(JS对象键本质是字符串,数字键会自动转字符串)。

接口继承 :接口可以通过extends关键字继承其他接口,实现"契约规则的复用与扩展 "------子接口会包含父接口的所有属性,再新增自己的属性,不用重复定义。

  • 支持多继承 ,用,分隔多个父接口,一次性复用多个契约的规则,如interface User extends HasId, HasName
javascript 复制代码
// 定义一个Person接口,约定对象必须有name(string)和age(number)属性
interface Person {
  name: string;
  age: number;
}

// 符合接口契约的对象:类型匹配,可直接赋值
const user: Person = {
  name: "张三",
  age: 25
};

// 违反契约的错误示例
// const wrongUser: Person = {
//   name: "李四" // 报错:缺少age属性
//   // age: "26" // 报错:age类型应为number
// };

特性

javascript 复制代码
// 索引签名:键为string类型,值为string类型
interface DynamicObj {
  [key: string]: string;
  // 固定属性需兼容索引签名类型(值必须是string)
  name: string;
  readonly id: number; // 只读属性:初始化后不可改
  phone?: string; // 可选属性:可传可不传
}

const obj: DynamicObj = {
  id: 123,
  name: "张三",
  gender: "男", // 动态属性:符合索引签名
  address: "北京" // 动态属性:符合索引签名
};

// 错误示例:值类型不符合索引签名
// obj.age = 25; // 报错:age值为number,预期string
// obj = 1002; // 报错:只读属性不能修改

接口继承

javascript 复制代码
// 父接口:基础用户契约
interface Person {
  name: string;
  age: number;
}

// 子接口:学生契约,继承Person并新增grade属性
interface Student extends Person {
  grade: number; // 学生专属属性
}

// 符合Student契约:需包含Person的所有属性+Student的属性
const student: Student = {
  name: "王五",
  age: 18,
  grade: 12
};

// 错误示例:缺少父接口属性
// const wrongStudent: Student = { name: "赵六", grade: 11 }; // 报错:缺少age属性

3.接口与类型别名type的区别

总结:接口适合定义"对象/函数的结构契约",type适合定义"类型组合(联合、交叉)、基础类型别名"

  • 相同点:
    • 均可定义对象/函数类型 ,如 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 :通过**交叉类型(&)**扩展,如 type Student = User & { age: number }
    • 合并能力
      • Interface :支持"声明合并 "(同名接口会自动合并属性),如 interface User { name: string }interface User { age: number } 合并为 { name: string; age: number }
      • Type不支持声明合并(同名会报错)。
    • 适用范围
      • Interface 能定义对象/函数类型不能定义基础类型 (如 interface Num = number 报错)。
      • Type :可定义任意类型基础类型、联合类型、交叉类型 等),如 type StrOrNum = string | number
    • 使用场景
      • 优先用 Interface :定义组件 Props、API 返回数据结构等需"可扩展/合并"的对象类型(符合团队协作中类型迭代的需求)。
      • 优先用 Type :定义联合类型、交叉类型、基础类型别名 (如 type ID = string | number),或需要复用复杂类型时。

3.泛型

3.1. 为什么要引入泛型?

例如,定义一个 print 函数,这个函数的功能是把传入的参数打印出来,最后再返回这个参数

javascript 复制代码
// 需求:传入string,返回也是string
function print(arg: string): string{
	console.log(arg)
	return arg
}

//需求变了:还需要传入number,用联合类型做
function print(arg: string | number): string | number{
	console.log(arg)
	return arg
}

//需求再变:还需要打印string数组、number数组以及任意类型,使用any
function print(arg: any): any{
	console.log(arg)
	return arg
}
//这时问题就出来了。传入的是any,返回的也是any(传入的是string,返回的可能是任意类型),就会导致类型不匹配
const res: string = print(123)   //是不会报错的

因此,泛型就出现了,来解决输入输出要一致的问题。(当然泛型还解决了其他问题。)

为什么需要泛型(解决两个核心问题)

  • 类型安全 :避免使用 any 导致的类型丢失(如用 any 定义数组,无法约束数组元素类型)。
  • 代码复用一套逻辑支持多种类型(如一个"获取数组第一个元素"的函数,可同时支持 string 数组、number 数组)。

3.2. 泛型的基础使用

我们可以把泛型 比喻为一个类型占位符 ,它告诉编译器:"嘿,这里有一个类型参数,我现在不确定具体是什么类型,但稍后会告诉你。"

泛型定义 :泛型是"类型参数化 "的语法,允许在定义函数、接口、类不指定具体类型 ,而是在使用时动态传入类型 (类似函数的参数传递),语法用 <T>(T 为类型占位符 ,可自定义名称)。(泛型参数 <T> 的书写位置必须紧跟在类型名称(函数名、接口名、类名、类型别名)后面

javascript 复制代码
//使用泛型解决上面any遗留的问题,用T占位,可以传任意类型,输出也是对应类型
function print<T>(arg:T):T {
    console.log(arg)
    return arg
}
const res: string = print(123)   //此时,输入输出不一致是会报错的

使用时两种方式指定类型:

  • 定义要使用的类型
  • TS 类型推断,自动推导出类型
javascript 复制代码
print<string>('hello')  // 定义 T 为 string
print('hello')  // TS 类型推断,自动推导类型为 string

1⃣️、默认参数: 给泛型加默认参数(使用场景都可加)

javascript 复制代码
function identity<T = number>(value: T): T {
  return value;
}

2⃣️、处理多个函数参数

javascript 复制代码
//需求:传入一个只有两项的元组,交换元组的第 0 项和第 1 项,返回这个元组
function swap<T, U>(tuple: [T, U]): [U, T]{
    return [tuple[1], tuple[0]]
}

3⃣️、keyof :是一个类型操作符 ,用于获取对象类型所有属性键的联合类型。它将对象类型的属性键提升到类型层面,使得我们可以进行基于属性名的类型操作。

javascript 复制代码
interface Person {
  name: string;
  age: number;
  address: string;
}
type PersonKeys = keyof Person; // "name" | "age" | "address"

4⃣️、泛型约束 :需要对泛型参数施加限制 ,使其必须满足某些条件。可以通过 extends 关键字实现泛型约束。

javascript 复制代码
interface HasLength {
  length: number;
}
//T 必须具有 length 属性,否则会报错
function logLength<T extends HasLength>(arg: T) {
  console.log(arg.length);
}

项目中使用

javascript 复制代码
import axios from 'axios'
interface API {
    '/book/detail': {
        id: number,
    },
    '/book/comment': {
        id: number
        comment: string
    }
    ...
}
//T extends keyof API 作为泛型约束,确保类型参数 T 是类型 API 的有效属性名。
function request<T extends keyof API>(url: T, obj: API[T]) {
    return axios.post(url, obj)
}
request('/book/comment', {
    id: 1,
    comment: '非常棒!'
})

3.3.泛型的使用场景

1⃣️、函数中使用泛型

javascript 复制代码
function identity<T>(value: T): T {
  return value;
}

2⃣️、类型别名 type 中使用泛型‌

javascript 复制代码
type Print = <T>(arg: T) => T
const printFn: Print = function print(arg){	return arg	}

3⃣️、接口 interface 中使用泛型

javascript 复制代码
interface Iprint<T> {
    (arg: T): T
}
//给泛型加默认参数的写法
interface Iprint<T = number> {
    (arg: T): T
}
function print<T>(arg:T) {
    console.log(arg)
    return arg
}
const myPrint: Iprint<number> = print

项目中使用在请求API上

javascript 复制代码
interface UserInfo {
    name: string
    age: number
}
function request<T>(url:string): Promise<T> {
    return fetch(url).then(res => res.json())
}
request<UserInfo>('user/info').then(res =>{
    console.log(res)
})

4⃣️、类 class 中使用泛型‌

javascript 复制代码
class Box<T> {
  private value: T;
  constructor(value: T) {
    this.value = value;
  }
  getValue(): T {
    return this.value;
  }
}

四、TS相关面试题

1.TS是什么?与JS区别

TS :是JS的超集,在js基础上增加了静态类型系统,最终会编译成js运行。

区别

  • 类型:ts静态类型,编译时就检查类型js动态类型,运行时才确定类型
  • 编译阶段:ts 需要编译为js 再执行;js可直接执行
  • 开发体验:ts 支持工具vscode智能提示、自动补全、类型错误提前预警;js手动判断类型

2.TS数据类型

2.1.变量声明

变量声明常量名: 数据类型

2.2.数据类型分四大类

  • 基本类型string、number、boolean、symbol、bigint、null(空类型)、undefined(未定义类型)(继承js
  • 引用类型
    • array:写法数据类型 []Array<数据类型>
    • Tuple(元组):Array 的一种特殊情况。固定长度、固定类型的数组
    • object:使用{}定义 {属性名:数据类型;...}(直接:object,修改对象属性会出错,因为未设置属性类型)
    • function:写法function箭头函数,三种参数:
      • 可选参数: ? 配置可有可无的参数。 age?: number
      • 默认参数: = 设定默认值。age: number = 11
      • 剩余参数: ...,如...numbers: number[]
  • 特殊类型 :(TS独有的独立于基本数据类型 的一组补充类型 ‌)
    • any:任意类型,关闭类型检测,等同于JS无限制操作。
    • unknow:未知类型(安全的any),只能赋值给null/unknown。用于接收未知来源的值(用户输入、请求返回)
    • void:无返回值类型,只有undefined能赋值给它。用来定义无返回的函数(console)
    • nerver:永不存在的类型,不能操作的类型,不能赋值也不能接收。用来定义(抛出异常函数、无限循环函数)的返回值
    • Enum:枚举,定义带名字的常量,string(键值对形式)/number(默认从0递增,除非设置值)
  • 其他类型
    • 字面量类型限制值为特定字面量 ,目前支持字符串、数字、布尔 三种类型。let num: 1 | 2 | 3
      • 常用场景 :定义固定选项(如按钮类型、状态码、性别等),配合联合类型一起用。
    • 交叉类型(& :,用 & 分隔,变量的类型是"多个类型的合并 "。TypeA & TypeB & TypeC
    • 联合类型(| :用 | 分隔,变量的类型是"多个类型中的任意一个 "。TypeA | TypeB | TypeC
    • 类型别名 (typetype 给「复杂类型」起一个别名 ,一次定义,全局复用别名代替原类型。type 类型别名 = 具体类型;
    • 类型推论(TS自己判断) :默认推论为any类型
    • 类型断言 (as,人为告诉) :用as告诉TS变量的类型。变量 as 目标类型

2.3. 类型守卫

类型守卫定义 :在运行时检查变量的类型 ,让 TS 在代码块内部自动推断出变量的具体类型(缩小类型范围,避免类型错误)。

常用类型守卫方式

  • typeof 守卫 (判断基础类型string/number/boolean/symboltypeof value === "string"
  • instanceof 守卫 (判断引用类型:数组、类实例 等)data instanceof Array
  • in 守卫 (判断对象是否包含某个属性key in obj
  • 自定义类型守卫 (通过函数返回 param is Type 语法 自定义)function isUser(data: unknown): data is User

3.class类

  • constructor:声明类的构造函数
  • extends :继承父类,子类用super调用父类方法及属性
  • abstract :放在类前,叫抽象类;放在方法前,叫抽象方法。
    • 抽象类:只声明不能实例化;作为基类提供公共属性方法等;支持部分实现(具体方法(可实现);抽象方法(只声明,子类实现))
    • 抽象方法 :只声明(在抽象类中),没有方法体,子类必须实现所有抽象方法。 abstract makeSound(): void
  • 修饰符
    • static:静态属性/方法。
    • readonly :设置为只读 ,必须在声明时或构造函数中初始化
    • public :默认值,公开任意位置都可访问修改
    • private私有 。只能在类本身使用。若想访问,在类中添加方法
    • protected受保护的 。只能在类本身及子类中访问

4.接口 interface

作用定规则 ,规定对象必须有哪些属性及属性是什么类型

特性:

  • 可选属性 :用?标记属性 ,表示该属性"可有可无 ",适合对象属性不固定的场景
  • 只读属性 :用readonly标记属性 ,表示该属性仅能在对象初始化时赋值后续无法修改 ,适合ID、创建时间固定属性
  • 索引签名 :对象的属性名不固定,约束动态属性的键和值的类型 ,避免乱加属性。适用于后端传回的动态数据
    • 语法:[key: 键类型]: 值类型,键类型通常是string或number(JS对象键本质是字符串,数字键会自动转字符串)。

接口继承

  • extends继承其他接口,复用及扩展其他规则(子接口会包含父接口的所有属性,再新增自己的属性
  • 支持多继承 ,用,分隔多个父接口,一次性复用多个契约的规则,如interface User extends HasId, HasName

4.1.接口与类型别名type的区别

相同点: 都可定义对象/函数类型 ;都支持泛型
不同点:

  • 扩展方式interface通过 extends 扩展type通过**交叉类型(&)**扩展
  • 合并能力interface支持"声明合并 "(同名接口会自动合并属性);type不支持声明合并(同名会报错)
  • 适用范围interface只能定义对象/函数类型type定义任意类型基础类型、联合类型、交叉类型等)
  • 使用场景interface定义组件 Props、API 返回数据结构等需"可扩展/合并"的对象类型type定义联合类型、交叉类型、基础类型别名 ,或需要复用复杂类型时。

5.TypeScript 的模块系统(import/export)与 JavaScript 的模块系统有什么区别?

  • 共同点:
    • 均支持 ES 模块import/export)和 CommonJS 模块require/module.exports),语法一致。
    • 均通过模块隔离代码(避免全局变量污染)。
  • 不同点:
    • 导出/导入内容: js只能导出/导入**"值"(变量、函数、对象等);ts还可 额外导出/导入"类型"**(接口、类型别名、泛型等),用 export type/import type 语法;
    • 模块解析: ts支持更灵活的模块解析(如 baseUrl、paths 配置,可简化导入路径),需在 tsconfig.json 中配置
    • 类型检查: js仅在运行时 才会发现导入错误ts导入模块时会检查"导入的类型是否存在"

6.TypeScript 中的"命名空间(Namespace)"是什么?它与模块(Module)的区别是什么?

命名空间定义 :用 namespace 关键字定义,用于将相关的类型、函数、变量封装在一个命名空间内 ,通过 export 暴露内部成 员,避免全局命名冲突

javascript 复制代码
// 定义命名空间(可嵌套)
namespace MathUtils {
  export function add(a: number, b: number): number {
    return a + b;
  }
  // 未 export 的成员仅在命名空间内可见
  function subtract(a: number, b: number): number {
    return a - b;
  }
}
// 使用命名空间成员(通过 命名空间.成员 访问)
MathUtils.add(1, 2); // 3

区别

特性 命名空间(Namespace) 模块(Module)
文件关联 可在单个文件 中定义,支持跨文件合并 每个文件就是一个独立模块
依赖管理 无内置依赖管理 ,需手动通过<reference>引入 支持 import/export 管理依赖
作用域 全局作用域内的一个容器(可能污染全局) 独立作用域(文件内声明默认不暴露)
现代项目适用性 适用于早期非模块化 项目(如全局脚本 适用于现代模块化项目(推荐使用)

7.使用 TypeScript 时,你遇到过哪些印象深刻的问题?是如何解决的?(项目)

  • API 返回数据类型不匹配
    • 问题 :用户列表组件,定义了 User 接口(包含 id、name、age),但调用 API 后,TS 报错类型"undefined"不能赋值给类型"number "'。排查后发现,API 返回的 age 字段在部分用户数据 中是 undefined(因用户未填写年龄 ),但我定义的 User 接口age必选的 number 类型 ,导致类型不匹配
    • 解决
      • 先检查 API 文档,确认 age可选字段(可能为 undefined);
      • 修改 User 接口,将 age 改为可选属性age?: number);
      • 在组件中使用 age 时,添加空值判断(如 user.age || 0),避免渲染错误
  • 第三方 JS 库无 TypeScript 类型
    • 问题 :项目需要引入 一个老的日期格式化 JS 库 (·date-format-utils.js·),但该库无官方类型声明,导入后 TS 报错无法找到模块"date-format-utils"的声明文件'。
    • 解决
      • 先尝试安装官方声明文件npm install @types/date-format-utils --save-dev),但发现不存在;
      • 手动创建声明文件 date-format-utils.d.ts,在文件中声明该模块的类型 (如 declare module "date-format-utils" { export function format(date: Date, pattern: string): string; });
      • 导入库时,TS 能识别声明的类型,可安全调用 format 方法,且有智能提示。

8.泛型

1⃣️、定义: 允许在定义函数、接口、类 式不指定具体类型 ,而是在使用时动态传入类型 (类似函数的参数传递),语法用 <T>(T 为类型占位符 ,可自定义名称)。可以设置默认参数 <T = number>;也可设置多参数 <T,k>

2⃣️、使用时两种方式指定类型:

  • 定义要使用的类型
  • TS 类型推断,自动推导出类型

3⃣️、keyof :是一个类型操作符 ,用于获取对象类型所有属性键的联合类型

4⃣️、泛型约束 :需要对泛型参数施加限制 ,使其必须满足某些条件。可以通过 extends 关键字实现泛型约束。

5⃣️、泛型使用场景

  • 函数中
  • 类型别名type中
  • 接口interface
  • 类class

原文链接:https://blog.csdn.net/Javachichi/article/details/156054946

相关推荐
晓13133 小时前
第三章 TypeScript 高级类型
前端·javascript·typescript
晓13135 小时前
第四章 TypeScript 类型声明文件与 React 运用
前端·react.js·typescript
~欲买桂花同载酒~5 小时前
项目安装- React + TypeScript
前端·react.js·typescript
ssshooter5 小时前
infer,TS 类型系统的手术刀
前端·面试·typescript
We་ct5 小时前
LeetCode 162. 寻找峰值:二分高效求解
前端·算法·leetcode·typescript·二分·暴力
MIka1 天前
CopilotKit 入门:用 Runtime 和 React Core 搭建真正可用的 AI Copilot
人工智能·typescript·agent
学以智用1 天前
# TypeScript 高级特性(核心+实用)
前端·javascript·typescript
学以智用1 天前
TypeScript 核心基础:类型/变量 + 函数 + 接口
前端·javascript·typescript
蕨类植物1 天前
Fastify 模块化项目实战(二) — 初始化Fastify 项目
typescript