【TypeScript】初识,基础类型,以及interface和type的使用

目录

    • [一、TypeScript 是什么](#一、TypeScript 是什么)
      • [1.1 为什么需要它?](#1.1 为什么需要它?)
      • [1.2 它解决了什么?](#1.2 它解决了什么?)
      • [1.3 它怎么工作的?](#1.3 它怎么工作的?)
      • [1.4 与 Vue 的关系](#1.4 与 Vue 的关系)
    • 二、基础类型
      • [2.1 string / number / boolean](#2.1 string / number / boolean)
      • [2.2 array ------ 数组](#2.2 array —— 数组)
    • 三、对象的类型定义:Interface
      • [3.1 什么是 Interface](#3.1 什么是 Interface)
      • [3.2 关于"多余属性"的两个情况](#3.2 关于“多余属性”的两个情况)
    • [四、Interface 与 Type 的区别](#四、Interface 与 Type 的区别)
      • [4.1 基础用法对比](#4.1 基础用法对比)
      • [4.2 核心区别一览](#4.2 核心区别一览)
      • [4.3 实战分工(黄金法则)](#4.3 实战分工(黄金法则))
    • [五、类(Class)与抽象类(Abstract Class)](#五、类(Class)与抽象类(Abstract Class))
      • [5.1 普通类(具体类)](#5.1 普通类(具体类))
      • [5.2 抽象类(Abstract Class)](#5.2 抽象类(Abstract Class))
      • [5.3 Interface、抽象类、具体类的终极对比](#5.3 Interface、抽象类、具体类的终极对比)
    • [六、类与接口的关系:`extends` 与 `implements`](#六、类与接口的关系:extendsimplements)
      • [6.1 Interface 与 Interface:`extends`](#6.1 Interface 与 Interface:extends)
      • [6.2 类与 Interface:`implements`](#6.2 类与 Interface:implements)
      • [6.3 类与类(或抽象类):`extends`](#6.3 类与类(或抽象类):extends)
      • [6.4 总览速查表](#6.4 总览速查表)
    • 七、函数参数与返回值的类型标注
      • [7.1 基本写法](#7.1 基本写法)
      • [7.2 箭头函数与回调](#7.2 箭头函数与回调)
    • 八、初识泛型(Generic)
      • [8.1 泛型是什么](#8.1 泛型是什么)
      • [8.2 Vue 3 中的 `Ref<T>`](#8.2 Vue 3 中的 Ref<T>)
      • [8.3 多个泛型参数](#8.3 多个泛型参数)
    • 九、开发最佳实践(怎么选?)

一、TypeScript 是什么

TypeScript(简称 TS)由微软开发,2012 年首次发布,创始人是 Anders Hejlsberg(也是 C# 和 Turbo Pascal 的创造者)。

它的本质是 JavaScript 的超集 ,通俗来说就是 JavaScript + 类型系统

1.1 为什么需要它?

JavaScript 存在几个先天痛点:

  1. 运行时才报错 :代码不运行就发现不了 null 取值错误。
  2. 参数无约束:函数本意加数字,传入字符串却变成拼接,结果完全错误。
  3. 对象结构模糊:接手一个对象,里面有什么字段全靠猜或翻文档。

1.2 它解决了什么?

JavaScript 的问题 TypeScript 的解决
变量类型随时可变 类型固定,赋值错误立刻报错
函数参数无约束 参数类型明确,传错立即红线
对象字段靠猜 接口定义一目了然,IDE 自动补全
重构时遗漏修改 全部标红,一个不漏

一句话:把运行时错误,提前到写代码的开发阶段发现。

1.3 它怎么工作的?

TS 不能直接在浏览器运行,需要编译:

typescript 复制代码
// 你写的 .ts 文件
function add(a: number, b: number): number {
  return a + b;
}

// 编译后变成 .js(类型全消失)
function add(a, b) {
  return a + b;
}

核心特性:类型标注只在编译阶段存在,运行后全部擦除,不增加任何运行时负担。

1.4 与 Vue 的关系

Vue 3 本身用 TypeScript 编写,其 API(如 ref / computed)天然支持 TS。使用 TS 后,IDE 能精准提示 value 的类型,彻底杜绝拼写错误(如把 name 写成 nme)。


二、基础类型

2.1 string / number / boolean

typescript 复制代码
let name: string = 'Alice';
let age: number = 25;
let isDone: boolean = false;

类型推导:如果声明时赋值,TS 会自动推导类型,不必手写。

typescript 复制代码
let name = 'Alice'; // 推导为 string
// name = 123;      // ❌ 报错

2.2 array ------ 数组

typescript 复制代码
// 写法一
let names: string[] = ['Alice', 'Bob'];
// 写法二(泛型语法)
let scores: Array<number> = [90, 85];

// 联合类型数组(元素可以是多种类型)
let mixed: (string | number)[] = ['Alice', 25];

三、对象的类型定义:Interface

3.1 什么是 Interface

interface 是用来描述对象结构 的"蓝图"。它只存在于 TypeScript 编译阶段,编译后彻底消失,不产生任何 JS 代码。

typescript 复制代码
interface User {
  id: number;
  name: string;
  age?: number; // ? 表示可选属性
}

// 使用
const user: User = { id: 1, name: 'Alice' }; // age 可省略

注意 :Interface 只能描述"长什么样",绝对不能包含具体逻辑实现(如 console.log)。它内部的方法只能是"纯虚"的(只有签名,没有函数体)。

typescript 复制代码
interface Animal {
  eat(food: string): void; // ✅ 只有签名
  // eat(food: string): void {} // ❌ 报错!不能有实现
}

3.2 关于"多余属性"的两个情况

Interface 约束对象时,有一个重要的坑:直接写对象字面量先赋值给变量,检查严格程度不同。

typescript 复制代码
interface User { name: string; }

// 情况一:直接写(触发多余属性检查)❌ 报错
const a: User = { name: 'Alice', age: 25 }; 

// 情况二:先赋值给变量(结构类型检查)✅ 合法
const temp = { name: 'Alice', age: 25 };
const b: User = temp; 

虽然情况二合法,但在实际开发中,强烈建议保持 Interface 与对象完全一致,不要把多余字段悄悄带进来。


四、Interface 与 Type 的区别

type(类型别名)是另一种定义类型的方式,与 interface 高度重叠,但各有侧重。

4.1 基础用法对比

typescript 复制代码
// interface
interface User { name: string; }

// type
type User = { name: string; };

4.2 核心区别一览

能力 interface type
定义对象结构 ✅ 主力 ✅ 可以
定义基本类型别名(如 string ❌ 不能 type ID = string
定义联合类型(` `) ❌ 不能
定义元组(固定长度数组) ❌ 不能 type Point = [number, number]
同名自动合并 ✅ 会合并 ❌ 重名报错
扩展/继承 extends(冲突报错,安全) &(交叉类型,冲突变 never

4.3 实战分工(黄金法则)

对象结构用 interface,联合/元组/基础别名用 type。两者是"主体与零件"的互补关系,不是二选一。

typescript 复制代码
// 用 type 定义零件
type Theme = 'dark' | 'light';
type UserId = string;

// 用 interface 搭主体
interface User {
  id: UserId;
  theme: Theme;
}

五、类(Class)与抽象类(Abstract Class)

interface 不同,class 是 ES6 引入的 JavaScript 真实实体 ,编译后依然存在。在 TypeScript 中,class 拥有双重身份 :既是一个 (运行时可以 new 造对象),也是一个类型(编译时可以做类型检查)。

5.1 普通类(具体类)

普通类所有方法都有具体实现,可以直接 new 出实例。

typescript 复制代码
class Dog {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  bark(): void {
    console.log('汪汪');
  }
}

const dog = new Dog('旺财'); // ✅ 可以 new

5.2 抽象类(Abstract Class)

抽象类是用来被继承的半成品。它有两个特点:

  1. 不能直接 new 实例化(因为它是毛坯房,不能住人)。
  2. 内部可以既有已实现的方法 (给子类复用),也有抽象方法(逼迫子类必须自己实现)。
typescript 复制代码
abstract class Animal {
  // 已实现的方法(子类直接用)
  eat(): void {
    console.log('吃东西');
  }

  // 抽象方法(只有签名,没有实现)
  abstract makeSound(): void;
}

// const a = new Animal(); // ❌ 报错!抽象类不能 new

class Cat extends Animal {
  // 必须实现 makeSound,否则 TS 报错
  makeSound(): void {
    console.log('喵喵');
  }
}

const cat = new Cat();
cat.eat();        // 输出:吃东西(直接用父类的)
cat.makeSound();  // 输出:喵喵(自己被迫填的坑)

5.3 Interface、抽象类、具体类的终极对比

比较维度 Interface 抽象类 具体类
能否写具体逻辑(方法体)? ❌ 绝对不能 ✅ 部分可以 ✅ 全部可以
能否被 new 实例化? ❌ 编译后消失 ❌ 不能(半成品) ✅ 能(成品)
能否强迫子类实现某方法? ✅ 可以(但只是约束) ✅ 可以(abstract 方法强制实现) ❌ 不能
编译后是否存在? ❌ 彻底消失 ✅ 保留(JS 函数) ✅ 保留(JS 函数)

六、类与接口的关系:extendsimplements

这是你之前追问的核心。在 TS 中,extends(继承)implements(实现) 有严格的适用对象。

6.1 Interface 与 Interface:extends

Interface 可以继承另一个 Interface,且支持多继承

typescript 复制代码
interface Animal { name: string; }
interface Swimmer { swim(): void; }

// 单继承
interface Dog extends Animal { bark(): void; }

// 多继承(逗号分隔)
interface Frog extends Animal, Swimmer { jump(): void; }

特别能力 :Interface 甚至可以 extends 一个普通类,从而提取该类的实例类型结构。

typescript 复制代码
class Car { brand: string = 'Tesla'; }

interface ElectricCar extends Car {
  charge(): void;
}
// 此时 ElectricCar 自动包含 brand: string

6.2 类与 Interface:implements

类想要拥有 Interface 定义的形状,不能使用 extends ,必须使用 implements

typescript 复制代码
interface Flyable { fly(): void; }

// ❌ class Bird extends Flyable {} // 报错!
// ✅ 正确写法:
class Bird implements Flyable {
  fly() { console.log('飞'); }
}

一个类可以 implements 多个接口。

6.3 类与类(或抽象类):extends

类与类之间只能使用 extends(单继承)。

typescript 复制代码
// 继承普通类
class Parent { name: string; }
class Child extends Parent {}

// 继承抽象类(必须实现抽象方法)
abstract class Base { abstract init(): void; }
class Concrete extends Base { init() {} }

6.4 总览速查表

左侧(定义者) 关键字 右侧(被依赖者) 是否允许
Interface extends Interface / 类(提取类型) ✅ 允许
类(Class) extends 普通类 / 抽象类 ✅ 允许
类(Class) extends Interface ❌ 不允许(请改用 implements
类(Class) implements Interface / 类型别名(对象形状) ✅ 允许

七、函数参数与返回值的类型标注

7.1 基本写法

typescript 复制代码
// 普通函数
function add(a: number, b: number): number {
  return a + b;
}

// 无返回值(void)
function log(msg: string): void {
  console.log(msg);
}

// 可选参数(?)
function createUser(name: string, age?: number): void {
  console.log(name, age ?? '未知');
}

7.2 箭头函数与回调

typescript 复制代码
const double = (n: number): number => n * 2;

// 回调函数作为参数
function process(arr: number[], callback: (item: number) => void): void {
  arr.forEach(callback);
}

八、初识泛型(Generic)

8.1 泛型是什么

泛型(<T>)可以理解为**"类型的变量""类型的函数参数"**。

它的核心作用是:用一个占位符(如 T)代替具体类型,确保函数/类/接口中不同位置(如参数与返回值,或存与取)的类型始终保持一致。

typescript 复制代码
// 没有泛型:只能为每种类型单独写一个函数
// 有泛型:一个函数搞定所有
function wrap<T>(value: T): T {
  return value;
}

const a = wrap('hello'); // T 被推导为 string
const b = wrap(123);     // T 被推导为 number

8.2 Vue 3 中的 Ref<T>

你在 Vue 中天天用的 ref,就是泛型的经典应用。

typescript 复制代码
import { ref } from 'vue';

// 显式指定类型
const count = ref<number>(0); 
// count.value 的类型被锁定为 number

// 复杂类型
interface User { name: string; }
const user = ref<User | null>(null);
// 使用时,TS 知道 user.value 可能是 User 或 null

什么时候必须手动写 <类型>

当初始值无法推导出完整类型时,比如 null 或空数组 []

typescript 复制代码
// 必须手动指定
const todos = ref<TodoItem[]>([]); 
// 否则 TS 会推导为 never[],永远无法往里 push 东西

8.3 多个泛型参数

typescript 复制代码
function merge<T, U>(a: T, b: U): [T, U] {
  return [a, b];
}
const result = merge<string, number>('Alice', 25);
// result 类型为 [string, number]

九、开发最佳实践(怎么选?)

在实际编码时,按照这个决策流来:

  1. 定义对象/类结构 (如 API 返回、组件 Props、Store 状态)?

    优先用 interface(错误提示清晰,可声明合并)。

  2. 定义联合类型(|)、元组([a,b])或基础类型别名

    只能用 type

  3. 需要提供代码复用(公共方法)并强制子类填空

    abstract class

  4. 在 Vue 3 组合式 API 中造数据

    忘掉 class ,用 interface 定义结构,用普通函数返回对象字面量 {}(这对 Vue 的响应式系统最友好)。